purpose of notebook

(-) test and play with advanced numerical EDA methods

todos: (-) …

information

name: makeovermonday_2021w22 link: https://data.world/makeovermonday/2021w22 title: 2021/W22: The Plastic Waste Makers Index Data Source: Minderoo from 2019

insights
  1. correlation - most of the columns are highly correlated, that was to be expected, since most variables are depend on each other, e.g., rigid + flexible = total c production, total -> -rank rigid overall has a less strong correlation with the other variables, which might hint to there being a a different sub-population based on rigid production, flexible has a stronger correlation with total as rigid and total, since flexible is a far bigger contribution to total flexible has a stronger correlation with the overall production, than rigid, this is interesting and also might hint to rigid producers have a different market strategy than flexible producers
  2. a Gini coefficient of 0.56 is quite high, which means that only a top few producers are responsible for a large amount of the total SUP waste contribution, in other words 10% are responsible for 44% of the waste
  3. I need more knowledge how to work with and interpret SOM and PCA, maybe also not enough observations in data set,
    but the clustering from SOM, when only independent variables are used, actually show the two assumed subpopulations based on rigid vs flexible production
  4. parallel coordinate plot is not very insightful, since there is so much data close together, we cannot see any grouping without prior knowledge, only hint is when you zoom in at the bulk of the data in the lower third (y direction), there is a negative correlation
  5. remind that the difference with the partition by k-means is that for hierarchical clustering, the number of classes is not specified in advance
    it seems that most clusters are confused by some specific values (81, 63, 72, 36?), are they some sort of outlier, or what is special about them?
  6. As a reminder, this method aims at partitioning n observations into k clusters in which each observation belongs to the cluster with the closest average, serving as a prototype of the cluster works not quite as well as SOM, but very close, also recommend cluster is three, but closely followed by 2, since 3 does not reveal any significant connection right now, I wonder what that shows
references
  1. https://iamciera.github.io/SOMexample/html/SOM_RNAseq_tutorial_part2a_SOM.html
  2. https://statsandr.com/blog/clustering-analysis-k-means-and-hierarchical-clustering-by-hand-and-in-r/#optimal-number-of-clusters
  3. https://lukedaniels1.github.io/Bio381_2018/Daniels_Cluster_Analysis_Lecture.html
  4. https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92
load packages & global options
overview
library(tidyverse) # tidy data frame
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ---------------------------------------------------------------------------------------------------------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.2     v dplyr   1.0.6
v tidyr   1.1.3     v stringr 1.4.0
v readr   1.4.0     v forcats 0.5.1
-- Conflicts ------------------------------------------------------------------------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(ggthemes) # for extra plot themes
library(plotly) # make ggplots interactive

Attache Paket: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout
library(factoextra) # provides some easy-to-use functions to extract and visualize the output of multivariate data analyses
Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
# individual libraries are in the according cell

knitr::opts_chunk$set(
  # fig.width = 15, fig.height = 9, 
  warning = FALSE
)

# plotly: ,width = 900, height = 550
head(plastic)
correlation
most of the columns are highly correlated, that was to be expected, since most variables are depend on each other, e.g., rigid + flexible = total c production, total -> -rank rigid overall has a less strong correlation with the other variables, which might hint to there being a a different sub-population based on rigid production, flexible has a stronger correlation with total as rigid and total, since flexible is a far bigger contribution to total flexible has a stronger correlation with the overall production, than rigid, this is interesting and also might hint to rigid producers have a different market strategy than flexible producers
%>% select(-rank, -total, -assets) can show a more clear picture by removing dependent variables
r summary(plastic)
polymer_producer rank no_of_assets production_of_in_scope_polymers flexible_format_contribution_to_sup_waste rigid_format_contribution_to_sup_waste Length:100 Min. : 1.00 Min. : 0.00 Min. : 0.200 Min. :0.000 Min. :0.000 Class :character 1st Qu.: 25.75 1st Qu.: 3.00 1st Qu.: 0.500 1st Qu.:0.100 1st Qu.:0.100 Mode :character Median : 50.50 Median : 6.00 Median : 0.900 Median :0.200 Median :0.200 Mean : 50.50 Mean :11.56 Mean : 1.805 Mean :0.538 Mean :0.416 3rd Qu.: 75.25 3rd Qu.:12.25 3rd Qu.: 1.700 3rd Qu.:0.500 3rd Qu.:0.500 Max. :100.00 Max. :82.00 Max. :11.600 Max. :4.700 Max. :4.500 total_contribution_to_sup_waste total_waste_div_production Min. :0.200 Min. :0.3000 1st Qu.:0.300 1st Qu.:0.4300 Median :0.450 Median :0.5000 Mean :0.950 Mean :0.5834 3rd Qu.:0.925 3rd Qu.:0.6900 Max. :5.900 Max. :1.0000

Lorenz curve & Gini coefficient

a Gini coefficient of 0.56 is quite high, which means that only a top few producers are responsible for a large amount of the total SUP waste contribution, in other words 10% are responsible for 44% of the waste

name = c('')
df <- plastic %>% select(-polymer_producer, - total_waste_div_production) %>% mutate(rank = -rank) %>% # change sign of rank to make it increase with the dependent variables
  rename( assets = no_of_assets, 
          product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste, 
          total = total_contribution_to_sup_waste) 


library(corrplot) # correlation plots
# https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html

cor <- cor(df)
cor_mtest <- cor.mtest(df, conf.level = 0.99) # combining correlogram with significance test
corrplot(cor, method = "number", order = 'hclust', addrect = 3, p.mat = cor_mtest$p, insig = "pch") # order = AOE, FPC, hclust + addrect


corrplot(cor, p.mat = cor_mtest$p, low = cor_mtest$lowCI, upp = cor_mtest$uppCI, order = 'hclust', sig.level = 0.01, tl.pos = 'd', addrect = 3, rect.col = 'navy', plotC = 'rect', cl.pos = 'n', insig = "p-value")

name = c('total_contribution_to_sup_waste', 'Producers')
df <- plastic %>% rename(value = total_contribution_to_sup_waste) %>% select(value) 

library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz

lorenzcurve <- df %>% 
  ggplot(aes(value)) +
    stat_lorenz(desc = FALSE) +
    coord_fixed() +
    geom_abline(linetype = 'dashed') +
    theme_minimal() +
    hrbrthemes::scale_x_percent() +
    hrbrthemes::scale_y_percent() +
    # hrbrthemes::theme_ipsum_rc() +
    annotate_ineq(df$value) +
    ggtitle(paste("Lorenz curve for", name[1], sep=" ")) 
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of", name[1], sep=" ")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" "))) 

lorenzcurve
principal component analysis colored by self organizing map cluster

I need more knowledge how to work with and interpret SOM and PCA, maybe also not enough observations in data set https://iamciera.github.io/SOMexample/html/SOM_RNAseq_tutorial_part2a_SOM.html

but the clustering from SOM, when only independent variables are used, actually show the two assumed subpopulations based on rigid vs flexible produciton

name = c('flexible_format_contribution_to_sup_waste', 'rigid_format_contribution_to_sup_waste', 'Producers', 'flexible', 'rigid')
df <- plastic %>% rename(flexible = flexible_format_contribution_to_sup_waste, rigid = rigid_format_contribution_to_sup_waste) %>% select(flexible, rigid) %>% pivot_longer(cols = c(flexible,rigid))

library(gglorenz) #transformations for plotting Lorenz curve, https://github.com/jjchern/gglorenz

# get ggplot standard colors for grouping, which are equally spaced hues around the color wheel, starting from 15
gg_color_hue <- function(n) {
  hues = seq(15, 375, length = n + 1)
  hcl(h = hues, l = 65, c = 100)[1:n]
}

lorenzcurve <- df %>% 
  ggplot(aes(x = value, color = name)) +
    stat_lorenz(desc = FALSE) +
    coord_fixed() +
    geom_abline(linetype = 'dashed') +
    theme_minimal() +
    hrbrthemes::scale_x_percent() +
    hrbrthemes::scale_y_percent() +
    # hrbrthemes::theme_ipsum_rc() +
    annotate_ineq(filter(df, name == name[4])$value, y = 0.95, colour = gg_color_hue(2)[1]) +
    annotate_ineq(filter(df, name == name[5])$value, y = 0.90, colour = gg_color_hue(2)[2]) +
    ggtitle(paste("compare Lorenz curve of", name[1], "and", name[2], sep=" ")) 
lorenzcurve <- ggplotly(lorenzcurve) %>% layout(yaxis = list(title = paste("cumulative percentage of<br>", name[1], "<br>", name[2], sep="")), xaxis = list(title = paste("cumulative percentage of", name[2], sep=" "))) 

lorenzcurve
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

# We need to normalize the data based on scale function because the variables are different scales; Normalization means subtracting mean from each observation and dividing with standard deviation. Check all the variables mean values are zero now

# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale

head(scale_data)
     product flexible rigid
[1,]    11.2      4.7   1.2
[2,]     9.3      4.7   0.9
[3,]    11.6      4.0   1.3
[4,]     5.1      0.2   4.5
[5,]     9.5      3.2   1.1
[6,]     8.8      3.3   0.8
library(kohonen) # functions to train self-organising maps (SOMs)

# principle component analysis
pca <- prcomp(scale_data, scale=FALSE)
summary(pca)
Importance of components:
                          PC1     PC2     PC3
Standard deviation     2.6049 0.56785 0.17091
Proportion of Variance 0.9507 0.04518 0.00409
Cumulative Proportion  0.9507 0.99591 1.00000
# visualize pcs results
# Contributions of variables to PC1
fviz_contrib(pca, choice = "var", axes = 1, top = 10)

# Contributions of variables to PC2
fviz_contrib(pca, choice = "var", axes = 2, top = 10)

# Control variable colors using their contributions to the principle axis
fviz_pca_var(pca, col.var="contrib",
             gradient.cols = c("#00AFBB", "#E7B800", "#FC4E07"),
             repel = TRUE # Avoid text overlapping
             ) + theme_minimal() + ggtitle("Variables - PCA")


# add back to original so everything is together
pca_scores <- data.frame(pca$x)
data_val <- cbind(df, pca_scores)

pca_plot <- ggplot(data_val, aes(x = PC1, y = PC2)) +
    geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
    geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
    geom_point(alpha = 0.75) + # point geom is used to create scatterplots
    theme_minimal()
pca_plot <- ggplotly(pca_plot) %>% layout()

pca_plot

# clustering is performed using the som() function on the scaled gene expression values.
set.seed(3)

# define a grid for the SOM and train
grid_size <- ncol(scale_data)
som_grid <- somgrid(xdim = grid_size, ydim = grid_size, topo = 'hexagonal')
som_model <- som(scale_data, grid = som_grid)
summary(som_model)
SOM of size 3x3 with a hexagonal topology and a bubble neighbourhood function.
The number of data layers is 1.
Distance measure(s) used: sumofsquares.
Training data included: 100 objects.
Mean distance to the closest unit in the map: 0.244.
# generate som plots after training
plot(som_model, type = 'mapping')

plot(som_model, type = 'codes')

# plot(som_model, type = 'counts')
# plot(som_model, type = 'dist.neighbours')
# plot(som_model, type = 'quality')
# plot(som_model, type = 'changes')

# further split the clusters into a smaller set of clusters using hierarchical clustering.
som_cluster <- cutree(hclust(dist(som_model$codes[[1]])), 2) # use hierarchical clustering to cluster the codebook vectors

plot(som_model, type="mapping", bgcol = som_cluster, main = "Clusters")
add.cluster.boundaries(som_model, som_cluster)


# attach the hierchal cluster to the larger dataset data_val.
gridSquare <- grid_size * grid_size
som_clusterKey <- data.frame(som_cluster)
som_clusterKey$unit_classif <- c(1:gridSquare)
data_val <- cbind(data_val,som_model$unit.classif,som_model$distances) %>% rename(unit_classif = 'som_model$unit.classif', distances = 'som_model$distances')
data_val <- merge(data_val, som_clusterKey, by.x = "unit_classif" )
head(data_val)

# plot pca with colored clusters
pcasom_plot <- ggplot(data_val, aes(x = PC1, y = PC2, color = factor(som_cluster), text = polymer_producer)) +
    geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
    geom_point(alpha = 0.75) + # point geom is used to create scatterplots
    theme_minimal()
pcasom_plot <- ggplotly(pcasom_plot) %>% layout()

pcasom_plot
parallel coordinate plot

parallel coordinate plot is not very insightful, since there is so much data close together, we cannot see any grouping without prior knowledge, only hint is when you zoom in at the bulk of the data in the lower third (y direction), there is a negative correlation

# two variables, continuous x, continuous y, show trend and distribution
name = c('production_of_in_scope_polymers', 'total_contribution_to_sup_waste')
df <- merge(plastic, data_val, by.x = 'polymer_producer')
df <- df %>% rename(x = production_of_in_scope_polymers, y = total_contribution_to_sup_waste, cluster = som_cluster, text = polymer_producer) %>% select(x, y, cluster, text) 

# https://ggplot2.tidyverse.org/reference/geom_smooth.html
point_plot <- df %>%
  ggplot(aes(x = x, y = y, color = factor(cluster))) +
    # geom_jitter(alpha = 0.5, size = 1) +
    geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
    geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
    geom_smooth(fill = "grey90") + # aids the eye in seeing patterns in the presence of overplotting
    geom_point(aes(text = text), alpha = 0.75) + # point geom is used to create scatterplots
    theme_minimal() +
    ggtitle(paste("trend of", name[2], "over", name[1], sep=" ")) 
Ignoring unknown aesthetics: text
point_plot <- ggplotly(point_plot) %>% layout(xaxis = list(showticklabels = FALSE))
stat_contour(): Zero contours were generatedkein nicht-fehlendes Argument f昼㹣r min; gebe Inf zur昼㹣ckkein nicht-fehlendes Argument f昼㹣r max; gebe -Inf zur昼㹣ck`geom_smooth()` using method = 'loess' and formula 'y ~ x'
pseudoinverse used at 8.786neighborhood radius 0.714reciprocal condition number  0There are other near singularities as well. 0.25pseudoinverse used at 8.786neighborhood radius 0.714reciprocal condition number  0There are other near singularities as well. 0.25
x_density_plot <- df %>%
  ggplot(aes(x = x, color = factor(cluster))) +
    stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
    # geom_histogram(binwidth = 1) +
    theme_minimal() 
x_density_plot <- ggplotly(x_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))

y_density_plot <- df %>%
  ggplot(aes(x = y, color = factor(cluster))) +
    stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
    # geom_histogram(binwidth = 1) +
    coord_flip() +
    theme_minimal() 
y_density_plot <- ggplotly(y_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))

# https://ggplot2.tidyverse.org/reference/geom_quantile.html
qualtile_plot <- df %>%
  ggplot(aes(x = x, y = y, color = factor(cluster))) +
    geom_quantile(alpha = 0.8) + # fits a quantile regression to the data and draws the fitted quantiles with lines
    theme_minimal() 
qualtile_plot <- ggplotly(qualtile_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE))
Smoothing formula not specified. Using: y ~ x
Smoothing formula not specified. Using: y ~ x
# merge figures into one plot, via subplots, https://plotly-r.com/arranging-views.html
sub1 <- subplot(x_density_plot, plotly_empty(), point_plot, y_density_plot, nrows = 2, margin = 0, heights = c(0.15, 0.85), widths = c(0.9, 0.1), shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
sub2 <- subplot(qualtile_plot, plotly_empty(), margin = 0, widths = c(0.9, 0.10), titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
fig <- subplot(sub1, sub2, nrows = 2, margin = 0, heights = c(0.8, 0.2), shareX = TRUE) %>% layout(xaxis = list(title = name[1]), yaxis = list(title = name[2]))
Can only have one: config
  
fig
k-means clustering

https://statsandr.com/blog/clustering-analysis-k-means-and-hierarchical-clustering-by-hand-and-in-r/#optimal-number-of-clusters As a reminder, this method aims at partitioning n observations into k clusters in which each observation belongs to the cluster with the closest average, serving as a prototype of the cluster works not quite as well as SOM, but very close, also recommend cluster is three, but closely followed by 2, since 3 does not reveal any significant connection right now, I wonder what that shows

name = c('plastic producers clustered by focus')
df <- merge(plastic, select(data_val, som_cluster, polymer_producer), by.x = 'polymer_producer') %>%
  mutate(som_cluster = as.character(som_cluster)) %>%
  rename(product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste,
          assets =  no_of_assets,
          cluster = som_cluster) %>% 
  select(assets, product, flexible, rigid, cluster) 

library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data

# https://www.r-graph-gallery.com/parallel-plot-ggally.html#custom
parcoord_plot <- ggparcoord(df,
           columns = 1:4, groupColumn = 5,
           scale='center', # scaling: standardize and center variables
           showPoints = TRUE,
           title = name,
           alphaLines = 0.3) +
  theme_minimal() 
parcoord_plot <- ggplotly(parcoord_plot) %>% layout(autosize=T)

parcoord_plot
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

library(NbClust) # determining the optimal number of clusters in a data set
library(cluster) # computes agglomerative hierarchical clustering of the dataset

# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
head(scale_data)
     product flexible rigid
[1,]    11.2      4.7   1.2
[2,]     9.3      4.7   0.9
[3,]    11.6      4.0   1.3
[4,]     5.1      0.2   4.5
[5,]     9.5      3.2   1.1
[6,]     8.8      3.3   0.8
kmeans_model <- kmeans(scale_data, centers = 2, nstart = 12) #  k-means clustering is done via the kmeans() function, with the argument centers that corresponds to the number of desired clusters
df_cluster <- tibble(df, cluster = as.factor(kmeans_model$cluster)) # store cluster in original data set as column
head(df_cluster)

# check quality of a k-means partition
quality <- kmeans_model$betweenss / kmeans_model$totss 
print(paste("quality of kmeans is BSS/TSS: ", format(round(quality,2), nsmall = 2)))
[1] "quality of kmeans is BSS/TSS:  0.74"
# find optimal number of clusters
fviz_nbclust(scale_data, kmeans, method = 'wss') + # Elbow method, needs scaled data
  # geom_vline(xintercept = 2, linetype = 2) + # add line for better visualization
  labs(subtitle = "Elbow method") # add subtitle


fviz_nbclust(scale_data, kmeans, method = 'silhouette') + # Silhouette method, needs scaled data
  labs(subtitle = "Silhouette method") # add subtitle


fviz_nbclust(df[, !names(df) %in% name], kmeans, # Gap statistics, needs original data ?
             nstart = 25,
             method = 'gap_stat',
             nboot = 100) + # reduce it for lower computation time, but less precise results
  labs(subtitle = "Gap statistics method")
Clustering k = 1,2,..., K.max (= 10): .. done
Bootstrapping, b = 1,2,..., B (= 100)  [one "." per sample]:
.................................................. 50 
.................................................. 100 

nbclust_out <- NbClust(data = df[, !names(df) %in% name], # NbClust needs the original data ?
                       distance = 'euclidean',
                       min.nc = 2, # minimum number of clusters
                       max.nc = 10, # maximum number of cluster
                       method = 'complete',
                       index = 'all')
NaNs wurden erzeugt
[1] "Frey index : No clustering structure in this data set"
*** : The Hubert index is a graphical method of determining the number of clusters.
                In the plot of Hubert index, we seek a significant knee that corresponds to a 
                significant increase of the value of the measure i.e the significant peak in Hubert
                index second differences plot. 
 

*** : The D index is a graphical method of determining the number of clusters. 
                In the plot of D index, we seek a significant knee (the significant peak in Dindex
                second differences plot) that corresponds to a significant increase of the value of
                the measure. 
 
******************************************************************* 
* Among all indices:                                                
* 7 proposed 2 as the best number of clusters 
* 9 proposed 3 as the best number of clusters 
* 1 proposed 5 as the best number of clusters 
* 2 proposed 6 as the best number of clusters 
* 2 proposed 9 as the best number of clusters 
* 2 proposed 10 as the best number of clusters 

                   ***** Conclusion *****                            
 
* According to the majority rule, the best number of clusters is  3 
 
 
******************************************************************* 

fviz_nbclust(nbclust_out) + theme_minimal() +
  labs(subtitle = "NbClust results")
Bedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutztBedingung hat L攼㸴nge > 1 und nur das erste Element wird benutzt
Among all indices: 
===================
* 2 proposed  0 as the best number of clusters
* 7 proposed  2 as the best number of clusters
* 9 proposed  3 as the best number of clusters
* 1 proposed  5 as the best number of clusters
* 2 proposed  6 as the best number of clusters
* 2 proposed  9 as the best number of clusters
* 2 proposed  10 as the best number of clusters
* 1 proposed  NA's as the best number of clusters

Conclusion
=========================
* According to the majority rule, the best number of clusters is  3 .

# check quality of clustering
# if a large majority of the silhouette coefficients are positive, it indicates that the observations are placed in the correct group
sil <- silhouette(kmeans_model$cluster, dist(scale_data)) 
fviz_silhouette(sil)


fviz_cluster(kmeans_model, df[, !names(df) %in% name], ellipse.type = 'norm') + theme_minimal()

fviz_cluster(kmeans_model, df[, !names(df) %in% name]) + theme_minimal()

name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale

kmean_calc <- function(df, ...){
  kmeans(df, scaled = ..., nstart = 30)
}

km2 <- kmean_calc(scale_data, 2)
km3 <- kmean_calc(scale_data, 3)
km4 <- kmeans(scale_data, 4)
km5 <- kmeans(scale_data, 5)
km6 <- kmeans(scale_data, 6)
km7 <- kmeans(scale_data, 7)

p1 <- fviz_cluster(km2, data = scale_data, ellipse.type = "convex") + theme_minimal()
p1 <- ggplotly(p1) %>% layout(annotations = list(text = "k = 2", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p2 <- fviz_cluster(km3, data = scale_data, ellipse.type = "convex") + theme_minimal()
p2 <- ggplotly(p2) %>% layout(annotations = list(text = "k = 3", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p3 <- fviz_cluster(km4, data = scale_data, ellipse.type = "convex") + theme_minimal()
p3 <- ggplotly(p3) %>% layout(annotations = list(text = "k = 4", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p4 <- fviz_cluster(km5, data = scale_data, ellipse.type = "convex") + theme_minimal()
p4 <- ggplotly(p4) %>% layout(annotations = list(text = "k = 5", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p5 <- fviz_cluster(km6, data = scale_data, ellipse.type = "convex") + theme_minimal()
p5 <- ggplotly(p5) %>% layout(annotations = list(text = "k = 6", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))
p6 <- fviz_cluster(km7, data = scale_data, ellipse.type = "convex") + theme_minimal()
p6 <- ggplotly(p6) %>% layout(annotations = list(text = "k = 7", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))

fig <- subplot(p1, p2, p3 , p4, p5, p6, nrows = 2, shareX = TRUE, shareY = TRUE) %>% layout() # TOOD: make all plots linked
fig
hierarchical clustering

https://statsandr.com/blog/clustering-analysis-k-means-and-hierarchical-clustering-by-hand-and-in-r/#optimal-number-of-clusters remind that the difference with the partition by k-means is that for hierarchical clustering, the number of classes is not specified in advance

it seems that most clusters are confused by some specific values (81, 63, 72, 36?), are they some sort of outlier, or what is special about them?

# two variables, continuous x, continuous y, show trend and distribution
name = c('production_of_in_scope_polymers', 'total_contribution_to_sup_waste')
df <- tibble(plastic, cluster = as.factor(kmeans_model$cluster))
df <- df %>% rename(x = production_of_in_scope_polymers, y = total_contribution_to_sup_waste, cluster = cluster, text = polymer_producer) %>% select(x, y, cluster, text) 

# https://ggplot2.tidyverse.org/reference/geom_smooth.html
point_plot <- df %>%
  ggplot(aes(x = x, y = y, color = factor(cluster))) +
    # geom_jitter(alpha = 0.5, size = 1) +
    geom_rug(alpha = 0.5) + # two 1d marginal distributions, display individual cases so are best used with smaller datasets
    geom_density_2d(alpha = 0.2, bins = 4) +# 2D kernel density estimation using MASS::kde2d() and display the results with contours
    geom_smooth(fill = "grey90") + # aids the eye in seeing patterns in the presence of overplotting
    geom_point(aes(text = text), alpha = 0.75) + # point geom is used to create scatterplots
    theme_minimal() +
    ggtitle(paste("trend of", name[2], "over", name[1], sep=" ")) 
Ignoring unknown aesthetics: text
point_plot <- ggplotly(point_plot) %>% layout(xaxis = list(showticklabels = FALSE))
stat_contour(): Zero contours were generatedkein nicht-fehlendes Argument f昼㹣r min; gebe Inf zur昼㹣ckkein nicht-fehlendes Argument f昼㹣r max; gebe -Inf zur昼㹣ck`geom_smooth()` using method = 'loess' and formula 'y ~ x'
x_density_plot <- df %>%
  ggplot(aes(x = x, color = factor(cluster))) +
    stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
    # geom_histogram(binwidth = 1) +
    theme_minimal() 
x_density_plot <- ggplotly(x_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))

y_density_plot <- df %>%
  ggplot(aes(x = y, color = factor(cluster))) +
    stat_density(geom="line") + # draws kernel density estimate, which is a smoothed version of the histogram
    # geom_histogram(binwidth = 1) +
    coord_flip() +
    theme_minimal() 
y_density_plot <- ggplotly(y_density_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE), xaxis = list(showticklabels = FALSE, showgrid = FALSE))

# https://ggplot2.tidyverse.org/reference/geom_quantile.html
qualtile_plot <- df %>%
  ggplot(aes(x = x, y = y, color = factor(cluster))) +
    geom_quantile(alpha = 0.8) + # fits a quantile regression to the data and draws the fitted quantiles with lines
    theme_minimal() 
qualtile_plot <- ggplotly(qualtile_plot) %>% layout(yaxis = list(showticklabels = FALSE, showgrid = FALSE))
Smoothing formula not specified. Using: y ~ x
Smoothing formula not specified. Using: y ~ x
# merge figures into one plot, via subplots, https://plotly-r.com/arranging-views.html
sub1 <- subplot(x_density_plot, plotly_empty(), point_plot, y_density_plot, nrows = 2, margin = 0, heights = c(0.15, 0.85), widths = c(0.9, 0.1), shareX = TRUE, shareY = TRUE, titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
sub2 <- subplot(qualtile_plot, plotly_empty(), margin = 0, widths = c(0.9, 0.10), titleX = FALSE, titleY = FALSE) %>% layout()
No trace type specified and no positional attributes specifiedNo trace type specified:
  Based on info supplied, a 'scatter' trace seems appropriate.
  Read more about this trace type -> https://plotly.com/r/reference/#scatter
No scatter mode specifed:
  Setting the mode to markers
  Read more about this attribute -> https://plotly.com/r/reference/#scatter-mode
Can only have one: config
fig <- subplot(sub1, sub2, nrows = 2, margin = 0, heights = c(0.8, 0.2), shareX = TRUE) %>% layout(xaxis = list(title = name[1]), yaxis = list(title = name[2]))
Can only have one: config
fig
Clustree
https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 In this figure the size of each node corresponds to the number of samples in each cluster, and the arrows are coloured according to the number of samples each cluster receives. A separate set of arrows, the transparent ones, called the incoming node proportion, are also coloured and shows how samples from one group end up in another group — an indicator of cluster instability.
```r name = c(‘polymer_producer’) df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other rename( product = production_of_in_scope_polymers, flexible = flexible_format_contribution_to_sup_waste, rigid = rigid_format_contribution_to_sup_waste)
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name])))) # scale_data <- as.matrix(scale(df[, !names(df) %in% name])) scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale head(scale_data) ```
product flexible rigid [1,] 11.2 4.7 1.2 [2,] 9.3 4.7 0.9 [3,] 11.6 4.0 1.3 [4,] 5.1 0.2 4.5 [5,] 9.5 3.2 1.1 [6,] 8.8 3.3 0.8
```r no_k = 2;
# Hierarchical clustering: single linkage hclust_res <- hclust(dist(scale_data), method = ‘single’) fviz_dend(hclust_res, k = no_k, rect = TRUE) ```
`guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.row names were found from a short variable and have been discarded
r # Hierarchical clustering: complete linkage hclust_res <- hclust(dist(scale_data), method = 'complete') plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = 'blue')
```r
# Hierarchical clustering: average linkage hclust_res <- hclust(dist(scale_data), method = ‘average’) plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = ‘blue’) ```
```r
# Hierarchical clustering: ward hclust_res <- hclust(dist(scale_data), method = ‘ward.D2’) fviz_dend(hclust_res, k = no_k, rect = TRUE) ```
`guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.row names were found from a short variable and have been discarded
r # Hierarchical clustering: mcquitty hclust_res <- hclust(dist(scale_data), method = 'mcquitty') plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = 'blue')
```r
# Hierarchical clustering: centroid hclust_res <- hclust(dist(scale_data), method = ‘centroid’) plot(hclust_res) rect.hclust(hclust_res, k = no_k, border = ‘blue’) ```

clValid to choose best clustering algo

https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 The cValid package can be used to simultaneously compare multiple clustering algorithms, to identify the best clustering approach and the optimal number of clusters. We will compare k-means, hierarchical and PAM clustering. Connectivity and Silhouette are both measurements of connectedness while the Dunn Index is the ratio of the smallest distance between observations not in the same cluster to the largest intra-cluster distance.

name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

# config
no_of_cluster = 9

library(clustree) # produce clustering trees, a visualization for interrogating clusterings as resolution increases
Lade n昼㸶tiges Paket: ggraph
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale
head(scale_data)
     product flexible rigid
[1,]    11.2      4.7   1.2
[2,]     9.3      4.7   0.9
[3,]    11.6      4.0   1.3
[4,]     5.1      0.2   4.5
[5,]     9.5      3.2   1.1
[6,]     8.8      3.3   0.8
tmp <- NULL
for (k in 1:no_of_cluster){
  tmp[k] <- kmeans(scale_data, k, nstart = 30)
}
Anzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴ngeAnzahl der zu ersetzenden Elemente ist kein Vielfaches der Ersetzungsl攼㸴nge
tmp_df <- data.frame(tmp)
colnames(tmp_df) <- seq(1:no_of_cluster) # add prefix to the column names
colnames(tmp_df) <- paste0("k", colnames(tmp_df)) 

# get individual PCA
tmp_df.pca <- prcomp(tmp_df, center = TRUE, scale. = FALSE)

ind.coord <- tmp_df.pca$x
ind.coord <- ind.coord[,1:2]

tmp_df <- bind_cols(as.data.frame(tmp_df), as.data.frame(ind.coord))

clustree(tmp_df, prefix = "k") # produce clustering trees, a visualization for interrogating clustering as resolution increases
The `add` argument of `group_by()` is deprecated as of dplyr 1.0.0.
Please use the `.add` argument instead.

overlay_list <- clustree_overlay(tmp_df, prefix = "k", x_value = "PC1", y_value = "PC2", plot_sides = TRUE)
overlay_list$overlay

overlay_list$x_side

overlay_list$y_side

Extracting Features of Clusters

https://towardsdatascience.com/10-tips-for-choosing-the-optimal-number-of-clusters-277e93d72d92 Ultimately, we would like to answer questions like “what is it that makes this cluster unique from others?” and “what are the clusters that are similar to one another”. Let’s select four clusters and interrogate the features of these clusters.

name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

library(clValid) # Statistical and biological validation of clustering results. This package implements Dunn Index, Silhouette, Connectivity, Stability, BHI and BSI
library(mclust) # BIC for parameterized Gaussian mixture models fitted by EM algorithm initialized by model-based hierarchical clustering
    __  ___________    __  _____________
   /  |/  / ____/ /   / / / / ___/_  __/
  / /|_/ / /   / /   / / / /\__ \ / /   
 / /  / / /___/ /___/ /_/ /___/ // /    
/_/  /_/\____/_____/\____//____//_/    version 5.4.7
Type 'citation("mclust")' for citing this R package in publications.

Attache Paket: 㤼㸱mclust㤼㸲

The following object is masked from 㤼㸱package:kohonen㤼㸲:

    map

The following object is masked from 㤼㸱package:purrr㤼㸲:

    map
# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale

intern <- clValid(scale_data, nClust = 2:9, 
                  clMethods = c("hierarchical", "kmeans", "diana", "fanny", "som", "model", "sota", "pam", "clara", "agnes"),
                  validation = "internal")
Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...Added defaults for somgrid object - you are probably using the somgrid function from the class library...
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
fitting ...

  |                                                                                                                                                                             
  |                                                                                                                                                                       |   0%
  |                                                                                                                                                                             
  |===========                                                                                                                                                            |   7%
  |                                                                                                                                                                             
  |======================                                                                                                                                                 |  13%
  |                                                                                                                                                                             
  |=================================                                                                                                                                      |  20%
  |                                                                                                                                                                             
  |=============================================                                                                                                                          |  27%
  |                                                                                                                                                                             
  |========================================================                                                                                                               |  33%
  |                                                                                                                                                                             
  |===================================================================                                                                                                    |  40%
  |                                                                                                                                                                             
  |==============================================================================                                                                                         |  47%
  |                                                                                                                                                                             
  |=========================================================================================                                                                              |  53%
  |                                                                                                                                                                             
  |====================================================================================================                                                                   |  60%
  |                                                                                                                                                                             
  |===============================================================================================================                                                        |  67%
  |                                                                                                                                                                             
  |==========================================================================================================================                                             |  73%
  |                                                                                                                                                                             
  |======================================================================================================================================                                 |  80%
  |                                                                                                                                                                             
  |=================================================================================================================================================                      |  87%
  |                                                                                                                                                                             
  |============================================================================================================================================================           |  93%
  |                                                                                                                                                                             
  |=======================================================================================================================================================================| 100%
rownames for data not specified, using 1:nrow(data)
summary(intern)

Clustering Methods:
 hierarchical kmeans diana fanny som model sota pam clara agnes 

Cluster sizes:
 2 3 4 5 6 7 8 9 

Validation Measures:
                                  2        3        4        5        6        7        8        9
                                                                                                  
hierarchical Connectivity    4.6155   7.5444  11.6909  14.9532  19.2865  22.4250  25.2083  29.7536
             Dunn            0.3720   0.3720   0.1929   0.1929   0.1929   0.2646   0.2704   0.2724
             Silhouette      0.8194   0.7297   0.7381   0.6527   0.6414   0.6302   0.6247   0.5973
kmeans       Connectivity   10.2603  12.2353  13.9532  26.8333  31.1667  27.6187  30.4020  43.1052
             Dunn            0.0382   0.1528   0.1929   0.0645   0.0645   0.0813   0.0821   0.0475
             Silhouette      0.7980   0.7422   0.7376   0.5689   0.5584   0.6131   0.6056   0.5357
diana        Connectivity    7.9532  13.0996  15.6984  16.7734  21.1067  29.7790  32.5623  33.6690
             Dunn            0.1283   0.1123   0.1536   0.2222   0.2327   0.0813   0.0912   0.1128
             Silhouette      0.8116   0.7116   0.6998   0.6916   0.6911   0.5705   0.5635   0.5625
fanny        Connectivity    5.3575  30.5325  20.0222  40.7393  31.4968  43.5433  57.6595  57.0190
             Dunn            0.0806   0.0121   0.0210   0.0211   0.0216   0.0216   0.0235   0.0242
             Silhouette      0.7644   0.5162   0.4707   0.3273   0.3990   0.3179   0.2899   0.3029
som          Connectivity   10.2603  12.2353  20.5857  34.8214  35.0476  42.3377  42.9198  44.1690
             Dunn            0.0382   0.1528   0.0542   0.0242   0.0242   0.0242   0.0235   0.0249
             Silhouette      0.7980   0.7422   0.6000   0.4521   0.4280   0.3946   0.4139   0.4297
model        Connectivity   18.0730  35.9623  33.8560  39.5698  47.6821  38.4135  64.5921 121.5214
             Dunn            0.0265   0.0091   0.0125   0.0171   0.0171   0.0529   0.0305   0.0331
             Silhouette      0.6085   0.3435   0.2001   0.2781   0.2833   0.2620   0.3482  -0.0495
sota         Connectivity    5.3575   9.9730  15.6730  21.8306  22.4734  23.8901  26.2234  30.3341
             Dunn            0.0806   0.1536   0.1536   0.1765   0.2327   0.2327   0.2327   0.2071
             Silhouette      0.7644   0.7469   0.7271   0.6824   0.6849   0.6841   0.6833   0.6189
pam          Connectivity    7.2980  12.2353  21.0063  29.3655  28.9500  30.6679  35.0012  38.4508
             Dunn            0.0784   0.1528   0.0211   0.0216   0.0216   0.0288   0.0288   0.0288
             Silhouette      0.7900   0.7422   0.4988   0.4323   0.4729   0.4794   0.4689   0.4507
clara        Connectivity   10.3147  16.0972  21.2472  29.6198  29.2012  38.0159  35.2159  37.8175
             Dunn            0.0746   0.0562   0.0216   0.0216   0.0235   0.0242   0.0305   0.0363
             Silhouette      0.8024   0.6976   0.4861   0.4355   0.4636   0.4693   0.4853   0.4787
agnes        Connectivity    4.6155   7.5444  11.6909  14.9532  19.2865  22.4250  25.2083  29.7536
             Dunn            0.3720   0.3720   0.1929   0.1929   0.1929   0.2646   0.2704   0.2724
             Silhouette      0.8194   0.7297   0.7381   0.6527   0.6414   0.6302   0.6247   0.5973

Optimal Scores:
NA
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

no_k = 3
no_var = 3

# scale_data <- as.matrix(t(scale(t(df[, !names(df) %in% name]))))
# scale_data <- as.matrix(scale(df[, !names(df) %in% name]))
scale_data <- as.matrix(df[, !names(df) %in% name]) # without scaling since the variables actually are on the same scale

# compute dissimilarity matrix with euclidean distances
d <- dist(scale_data, method = 'euclidean')

# hierarchical clustering using Ward's method
res_hc <- hclust(d, method = 'ward.D2')

# cut tree into 3 groups
grp <- cutree(res_hc, k = no_k)

# visualize
plot(res_hc, cex = 0.6) # plot tree
rect.hclust(res_hc, k = no_k, border = 2:5) # add rectangles


# execution of k-means with k = 4
final <- kmeans(scale_data, no_k, nstart = 30)

fviz_cluster(final, data = scale_data) + theme_minimal() + ggtitle("k = 4")

as.tibble(scale_data) %>% 
  mutate(cluster = final$cluster) %>%
  group_by(cluster) %>%
  summarise_all('mean')
`as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
as.tibble(plastic) %>% 
  mutate(cluster = final$cluster) %>%
  group_by(cluster) %>%
  summarise_all('mean')
argument is not numeric or logical: returning NAargument is not numeric or logical: returning NAargument is not numeric or logical: returning NA
df <- as_tibble(scale_data) %>% rownames_to_column()

cluster_pos <- as_tibble(final$cluster) %>% rownames_to_column()
colnames(cluster_pos) <- c("rowname", "cluster")

final <- inner_join(cluster_pos, df, by = "rowname")

library(ggiraphExtra) # enhance 'ggplot2' and 'ggiraph', provides functions for exploratory plots, see https://rpubs.com/cardiomoon/231820

Attache Paket: 㤼㸱ggiraphExtra㤼㸲

The following object is masked from 㤼㸱package:ggthemes㤼㸲:

    theme_clean
radar <- ggRadar(final[-1], aes(group = cluster), rescale = FALSE,
        legend.position = "none", size = 1, interactive = FALSE, use.label = TRUE) +
  facet_wrap(~cluster) +
  scale_y_discrete(breaks = NULL) + # don't show ticks
  theme_minimal()

radar

df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)

library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data

ggpairs(df, 1:no_var, mapping = ggplot2::aes(color = cluster, alpha = 0.5),
        diag = list(continuous = wrap("densityDiag")),
        lower = list(continuous = wrap("points", alpha = 0.9))) +
  theme_minimal()

df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)

box1 <- ggplot(df, aes(x = cluster, y = product, colour = cluster)) + 
    geom_boxplot() +
    theme_minimal() + 
    ggtitle("product")
box1 <- ggplotly(box1) %>% layout(annotations = list(text = "product", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))

box2 <- ggplot(df, aes(x = cluster, y = rigid, colour = cluster)) + 
    geom_boxplot() +
    theme_minimal() +
    ggtitle("rigid")
box2 <- ggplotly(box2) %>% layout(annotations = list(text = "rigid", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))

box3 <- ggplot(df, aes(x = cluster, y = flexible, colour = cluster)) + 
    geom_boxplot() +
    theme_minimal() +
    ggtitle("flexible")
box3 <- ggplotly(box3) %>% layout(annotations = list(text = "flexible", font = f, xref = "paper", yref = "paper", yanchor = "bottom", xanchor = "center", align = "center", x = 0.5, y = 1, showarrow = FALSE))

fig <- subplot(box1, box2, box3, margin = 0) %>% layout()

fig
compare scale functions

scale is generic function whose default method centers and/or scales the columns of a numeric matrix normally you scale the columns (ie variables) to have all variables on the same scale st an increase in 1 unit has the same meaning across all variables, the shape / distribution will not change, but the axis units (see scatter plots) scaling the rows (ie observations) made all observations equal in the sense that the absolute distances were replaced by relative distances across all observations, st now the biggest value in a row was across all rows equally big (see “dataframe” plot), in our case that was the production variable, now rigid and flexible were separated if there was more or less of them relative to the total production (biggest value, well actually to the mean not the max, but never mind), this sorted all observations according to be more on the rigid or flexible side, and that was excatly what I was looking for (and why I was too happy to see my mistake in the first place)

df <- as_tibble(scale_data)
df$cluster <- as.factor(final$cluster)

library(GGally) # extends ggplot2 by adding several functions to reduce the complexity of combining geoms with transformed data

# https://www.r-graph-gallery.com/parallel-plot-ggally.html#custom
parcoord_plot <- ggparcoord(df,
           columns = 1:no_var, groupColumn = (no_var+1),
           scale='center', # scaling: standardize and center variables
           showPoints = TRUE,
           title = name,
           alphaLines = 0.3) +
  theme_minimal() 
parcoord_plot <- ggplotly(parcoord_plot) %>% layout(autosize=T)

parcoord_plot
name = c('polymer_producer')
df <- plastic %>% select(- total_waste_div_production, -rank, -no_of_assets, -total_contribution_to_sup_waste) %>% # removed variables which are depended on each other
  rename( product = production_of_in_scope_polymers, 
          flexible = flexible_format_contribution_to_sup_waste, 
          rigid = rigid_format_contribution_to_sup_waste)

scale_data <- as_tibble(t(scale(t(df[, !names(df) %in% name])))) # We need to normalize the data based on scale function because the variables are different scales; Normalization means subtracting mean from each observation and dividing with standard deviation. Check all the variables mean values are zero now

head(scale_data)

scale_data <- as_tibble(scale(df[, !names(df) %in% name]))

head(scale_data)

head(t(df[, !names(df) %in% name]))
         [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] [,13] [,14] [,15] [,16] [,17] [,18] [,19] [,20] [,21] [,22] [,23] [,24] [,25] [,26] [,27] [,28] [,29]
product  11.2  9.3 11.6  5.1  9.5  8.8  9.3  5.5  6.7   2.5   5.0   4.1   4.8   4.5   1.7   1.6   3.6   3.4   3.1   1.4   1.9   2.4   2.4     1   2.0   1.0   2.2   1.6   1.8
flexible  4.7  4.7  4.0  0.2  3.2  3.3  2.1  1.8  1.9   0.0   1.5   1.1   1.0   1.0   0.0   0.0   1.0   1.2   1.1   0.0   1.0   0.9   0.6     0   0.7   0.0   0.5   0.7   0.7
rigid     1.2  0.9  1.3  4.5  1.1  0.8  1.8  1.3  1.1   2.3   0.7   1.0   1.0   0.9   1.6   1.6   0.6   0.3   0.3   1.3   0.2   0.3   0.5     1   0.3   0.9   0.4   0.2   0.1
         [,30] [,31] [,32] [,33] [,34] [,35] [,36] [,37] [,38] [,39] [,40] [,41] [,42] [,43] [,44] [,45] [,46] [,47] [,48] [,49] [,50] [,51] [,52] [,53] [,54] [,55] [,56] [,57]
product    1.2   1.7   1.3   1.9   1.6   1.4   1.0   1.1   1.2   0.6   1.0   1.5   1.1   1.2   0.5   1.1   1.2   0.6   0.5   1.1   1.0   1.0   0.4   0.9   0.8   0.9   0.7   0.6
flexible   0.2   0.5   0.4   0.6   0.6   0.5   0.5   0.2   0.5   0.0   0.5   0.4   0.4   0.5   0.0   0.4   0.4   0.0   0.0   0.3   0.4   0.3   0.0   0.3   0.3   0.3   0.3   0.3
rigid      0.7   0.4   0.4   0.2   0.1   0.2   0.1   0.4   0.1   0.6   0.1   0.1   0.2   0.1   0.5   0.1   0.1   0.5   0.5   0.2   0.1   0.1   0.4   0.1   0.1   0.1   0.0   0.1
         [,58] [,59] [,60] [,61] [,62] [,63] [,64] [,65] [,66] [,67] [,68] [,69] [,70] [,71] [,72] [,73] [,74] [,75] [,76] [,77] [,78] [,79] [,80] [,81] [,82] [,83] [,84] [,85]
product    0.4   0.8   0.7   0.6   0.9   0.4   0.6   0.7   1.0   0.6   0.8   0.3   0.7   0.7   0.5   0.5   0.6   0.4   0.3   0.7   0.7   0.7   0.4   0.4   0.6   0.5   0.5   0.5
flexible   0.0   0.2   0.3   0.1   0.2   0.0   0.1   0.2   0.2   0.2   0.1   0.0   0.2   0.2   0.0   0.2   0.2   0.2   0.0   0.2   0.2   0.2   0.2   0.3   0.2   0.2   0.2   0.2
rigid      0.4   0.1   0.1   0.2   0.1   0.3   0.2   0.1   0.1   0.1   0.2   0.3   0.1   0.1   0.2   0.1   0.1   0.0   0.3   0.1   0.1   0.1   0.0   0.0   0.0   0.1   0.0   0.1
         [,86] [,87] [,88] [,89] [,90] [,91] [,92] [,93] [,94] [,95] [,96] [,97] [,98] [,99] [,100]
product    0.2   0.6   0.4   0.5   0.5   0.6   0.5   0.2   0.2   0.2   0.5   0.5   0.6   0.2    0.2
flexible   0.0   0.2   0.2   0.1   0.1   0.2   0.2   0.0   0.0   0.0   0.1   0.1   0.1   0.0    0.0
rigid      0.2   0.1   0.0   0.1   0.1   0.1   0.0   0.2   0.2   0.2   0.1   0.1   0.1   0.2    0.2
library(caret)
preObj <- preProcess(df[, !names(df) %in% name], method=c("center", "scale"))
newData <- predict(preObj, df[, !names(df) %in% name])
head(newData)

# find more methods here: https://stackoverflow.com/questions/15215457/standardize-data-columns-in-r
LS0tDQp0aXRsZTogImRpc2NvdmVyIGFuZCB0cmFuc2Zvcm0gcGxhc3RpYyB3YXN0ZSBtYWtlcnMgaW5kZXggZGF0YSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCi0tLQ0KcHVycG9zZSBvZiBub3RlYm9vaw0KLS0tDQoNCiAgKC0pIHRlc3QgYW5kIHBsYXkgd2l0aCBhZHZhbmNlZCBudW1lcmljYWwgRURBIG1ldGhvZHMNCiAgDQp0b2RvczoNCiAgKC0pIC4uLg0KICANCi0tLQ0KaW5mb3JtYXRpb24NCi0tLQ0KDQpuYW1lOiBtYWtlb3Zlcm1vbmRheV8yMDIxdzIyDQpsaW5rOiBodHRwczovL2RhdGEud29ybGQvbWFrZW92ZXJtb25kYXkvMjAyMXcyMg0KdGl0bGU6IDIwMjEvVzIyOiBUaGUgUGxhc3RpYyBXYXN0ZSBNYWtlcnMgSW5kZXgNCkRhdGEgU291cmNlOiBbTWluZGVyb29dKGh0dHBzOi8vd3d3Lm1pbmRlcm9vLm9yZy9wbGFzdGljLXdhc3RlLW1ha2Vycy1pbmRleC9kYXRhL2luZGljZXMvcHJvZHVjZXJzLykgZnJvbSAyMDE5DQogIA0KLS0tDQppbnNpZ2h0cyANCi0tLQ0KDQogIChpKSBjb3JyZWxhdGlvbiAtIG1vc3Qgb2YgdGhlIGNvbHVtbnMgYXJlIGhpZ2hseSBjb3JyZWxhdGVkLCB0aGF0IHdhcyB0byBiZSBleHBlY3RlZCwgc2luY2UgbW9zdCB2YXJpYWJsZXMgYXJlIGRlcGVuZCBvbiBlYWNoIG90aGVyLCBlLmcuLCByaWdpZCArIGZsZXhpYmxlID0gdG90YWwgYyBwcm9kdWN0aW9uLCAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbCAtPiAtcmFuaw0KICAgICAgICAgICAgICAgICAgICByaWdpZCBvdmVyYWxsIGhhcyBhIGxlc3Mgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcywgd2hpY2ggbWlnaHQgaGludCB0byB0aGVyZSBiZWluZyBhIGEgZGlmZmVyZW50IHN1Yi1wb3B1bGF0aW9uIGJhc2VkIG9uIHJpZ2lkIHByb2R1Y3Rpb24sIA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRvdGFsIGFzIHJpZ2lkIGFuZCB0b3RhbCwgc2luY2UgZmxleGlibGUgaXMgYSBmYXIgYmlnZ2VyIGNvbnRyaWJ1dGlvbiB0byB0b3RhbA0KICAgICAgICAgICAgICAgICAgICBmbGV4aWJsZSBoYXMgYSBzdHJvbmdlciBjb3JyZWxhdGlvbiB3aXRoIHRoZSBvdmVyYWxsIHByb2R1Y3Rpb24sIHRoYW4gcmlnaWQsIHRoaXMgaXMgaW50ZXJlc3RpbmcgYW5kIGFsc28gbWlnaHQgaGludCB0byByaWdpZCBwcm9kdWNlcnMgaGF2ZSBhIGRpZmZlcmVudCBtYXJrZXQgICAgICAgICAgICAgICAgICAgICAgICBzdHJhdGVneSB0aGFuIGZsZXhpYmxlIHByb2R1Y2Vycw0KICAoaSkgYSBHaW5pIGNvZWZmaWNpZW50IG9mIDAuNTYgaXMgcXVpdGUgaGlnaCwgd2hpY2ggbWVhbnMgdGhhdCBvbmx5IGEgdG9wIGZldyBwcm9kdWNlcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIGFtb3VudCBvZiB0aGUgdG90YWwgU1VQIHdhc3RlIGNvbnRyaWJ1dGlvbiwgaW4gb3RoZXIgd29yZHMgMTAlICAgICAgICAgYXJlIHJlc3BvbnNpYmxlIGZvciA0NCUgb2YgdGhlIHdhc3RlDQogIChpKSBJIG5lZWQgbW9yZSBrbm93bGVkZ2UgaG93IHRvIHdvcmsgd2l0aCBhbmQgaW50ZXJwcmV0IFNPTSBhbmQgUENBLCBtYXliZSBhbHNvIG5vdCBlbm91Z2ggb2JzZXJ2YXRpb25zIGluIGRhdGEgc2V0LCAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgYnV0IHRoZSBjbHVzdGVyaW5nIGZyb20gU09NLCB3aGVuIG9ubHkgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFyZSB1c2VkLCBhY3R1YWxseSBzaG93IHRoZSB0d28gYXNzdW1lZCBzdWJwb3B1bGF0aW9ucyBiYXNlZCBvbiByaWdpZCB2cyBmbGV4aWJsZSBwcm9kdWN0aW9uDQogIChpKSBwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaXMgbm90IHZlcnkgaW5zaWdodGZ1bCwgc2luY2UgdGhlcmUgaXMgc28gbXVjaCBkYXRhIGNsb3NlIHRvZ2V0aGVyLCB3ZSBjYW5ub3Qgc2VlIGFueSBncm91cGluZyB3aXRob3V0IHByaW9yIGtub3dsZWRnZSwgb25seSBoaW50IGlzIHdoZW4geW91IHpvb20gaW4gYXQgICAgICAgdGhlIGJ1bGsgb2YgdGhlIGRhdGEgaW4gdGhlIGxvd2VyIHRoaXJkICh5IGRpcmVjdGlvbiksIHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24NCiAgKGkpIHJlbWluZCB0aGF0IHRoZSBkaWZmZXJlbmNlIHdpdGggdGhlIHBhcnRpdGlvbiBieSBrLW1lYW5zIGlzIHRoYXQgZm9yIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCB0aGUgbnVtYmVyIG9mIGNsYXNzZXMgaXMgbm90IHNwZWNpZmllZCBpbiBhZHZhbmNlICAgDQogICAgICBpdCBzZWVtcyB0aGF0IG1vc3QgY2x1c3RlcnMgYXJlIGNvbmZ1c2VkIGJ5IHNvbWUgc3BlY2lmaWMgdmFsdWVzICg4MSwgNjMsIDcyLCAzNj8pLCBhcmUgdGhleSBzb21lIHNvcnQgb2Ygb3V0bGllciwgb3Igd2hhdCBpcyBzcGVjaWFsIGFib3V0IHRoZW0/DQogIChpKSBBcyBhIHJlbWluZGVyLCB0aGlzIG1ldGhvZCBhaW1zIGF0IHBhcnRpdGlvbmluZyBuIG9ic2VydmF0aW9ucyBpbnRvIGsgY2x1c3RlcnMgaW4gd2hpY2ggZWFjaCBvYnNlcnZhdGlvbiBiZWxvbmdzIHRvIHRoZSBjbHVzdGVyIHdpdGggdGhlIGNsb3Nlc3QgYXZlcmFnZSwgc2VydmluZyBhcyBhIHByb3RvdHlwZSAgICAgICBvZiB0aGUgY2x1c3Rlcg0KICAgICAgd29ya3Mgbm90IHF1aXRlIGFzIHdlbGwgYXMgU09NLCBidXQgdmVyeSBjbG9zZSwgYWxzbyByZWNvbW1lbmQgY2x1c3RlciBpcyB0aHJlZSwgYnV0IGNsb3NlbHkgZm9sbG93ZWQgYnkgMiwgc2luY2UgMyBkb2VzIG5vdCByZXZlYWwgYW55IHNpZ25pZmljYW50IGNvbm5lY3Rpb24gcmlnaHQgbm93LCBJICAgICAgICAgICAgd29uZGVyIHdoYXQgdGhhdCBzaG93cw0KICAgICAgDQotLS0NCnJlZmVyZW5jZXMNCi0tLQ0KICANCiAgKGkpIGh0dHBzOi8vaWFtY2llcmEuZ2l0aHViLmlvL1NPTWV4YW1wbGUvaHRtbC9TT01fUk5Bc2VxX3R1dG9yaWFsX3BhcnQyYV9TT00uaHRtbA0KICAoaSkgaHR0cHM6Ly9zdGF0c2FuZHIuY29tL2Jsb2cvY2x1c3RlcmluZy1hbmFseXNpcy1rLW1lYW5zLWFuZC1oaWVyYXJjaGljYWwtY2x1c3RlcmluZy1ieS1oYW5kLWFuZC1pbi1yLyNvcHRpbWFsLW51bWJlci1vZi1jbHVzdGVycw0KICAoaSkgaHR0cHM6Ly9sdWtlZGFuaWVsczEuZ2l0aHViLmlvL0JpbzM4MV8yMDE4L0RhbmllbHNfQ2x1c3Rlcl9BbmFseXNpc19MZWN0dXJlLmh0bWwNCiAgKGkpIGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS8xMC10aXBzLWZvci1jaG9vc2luZy10aGUtb3B0aW1hbC1udW1iZXItb2YtY2x1c3RlcnMtMjc3ZTkzZDcyZDkyDQogICAgICANCi0tLQ0KbG9hZCBwYWNrYWdlcyAmIGdsb2JhbCBvcHRpb25zDQotLS0NCmBgYHtyLCBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIHRpZHkgZGF0YSBmcmFtZQ0KbGlicmFyeShnZ3RoZW1lcykgIyBmb3IgZXh0cmEgcGxvdCB0aGVtZXMNCmxpYnJhcnkocGxvdGx5KSAjIG1ha2UgZ2dwbG90cyBpbnRlcmFjdGl2ZQ0KDQpsaWJyYXJ5KGZhY3RvZXh0cmEpICMgcHJvdmlkZXMgc29tZSBlYXN5LXRvLXVzZSBmdW5jdGlvbnMgdG8gZXh0cmFjdCBhbmQgdmlzdWFsaXplIHRoZSBvdXRwdXQgb2YgbXVsdGl2YXJpYXRlIGRhdGEgYW5hbHlzZXMNCg0KIyBpbmRpdmlkdWFsIGxpYnJhcmllcyBhcmUgaW4gdGhlIGFjY29yZGluZyBjZWxsDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgIyBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDksIA0KICB3YXJuaW5nID0gRkFMU0UNCikNCg0KIyBwbG90bHk6ICx3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gNTUwDQpgYGANCg0KLS0tDQpvdmVydmlldw0KLS0tDQpgYGB7cn0NCmhlYWQocGxhc3RpYykNCmBgYA0KYGBge3J9DQpzdW1tYXJ5KHBsYXN0aWMpDQpgYGANCg0KLS0tDQpjb3JyZWxhdGlvbiANCi0tLQ0KbW9zdCBvZiB0aGUgY29sdW1ucyBhcmUgaGlnaGx5IGNvcnJlbGF0ZWQsIHRoYXQgd2FzIHRvIGJlIGV4cGVjdGVkLCBzaW5jZSBtb3N0IHZhcmlhYmxlcyBhcmUgZGVwZW5kIG9uIGVhY2ggb3RoZXIsIGUuZy4sIHJpZ2lkICsgZmxleGlibGUgPSB0b3RhbCBjIHByb2R1Y3Rpb24sIHRvdGFsIC0+IC1yYW5rDQpyaWdpZCBvdmVyYWxsIGhhcyBhIGxlc3Mgc3Ryb25nIGNvcnJlbGF0aW9uIHdpdGggdGhlIG90aGVyIHZhcmlhYmxlcywgd2hpY2ggbWlnaHQgaGludCB0byB0aGVyZSBiZWluZyBhIGEgZGlmZmVyZW50IHN1Yi1wb3B1bGF0aW9uIGJhc2VkIG9uIHJpZ2lkIHByb2R1Y3Rpb24sIA0KZmxleGlibGUgaGFzIGEgc3Ryb25nZXIgY29ycmVsYXRpb24gd2l0aCB0b3RhbCBhcyByaWdpZCBhbmQgdG90YWwsIHNpbmNlIGZsZXhpYmxlIGlzIGEgZmFyIGJpZ2dlciBjb250cmlidXRpb24gdG8gdG90YWwNCmZsZXhpYmxlIGhhcyBhIHN0cm9uZ2VyIGNvcnJlbGF0aW9uIHdpdGggdGhlIG92ZXJhbGwgcHJvZHVjdGlvbiwgdGhhbiByaWdpZCwgdGhpcyBpcyBpbnRlcmVzdGluZyBhbmQgYWxzbyBtaWdodCBoaW50IHRvIHJpZ2lkIHByb2R1Y2VycyBoYXZlIGEgZGlmZmVyZW50IG1hcmtldCBzdHJhdGVneSB0aGFuIGZsZXhpYmxlIHByb2R1Y2Vycw0KDQolPiUgc2VsZWN0KC1yYW5rLCAtdG90YWwsIC1hc3NldHMpIGNhbiBzaG93IGEgbW9yZSBjbGVhciBwaWN0dXJlIGJ5IHJlbW92aW5nIGRlcGVuZGVudCB2YXJpYWJsZXMNCg0KYGBge3J9DQpuYW1lID0gYygnJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtcG9seW1lcl9wcm9kdWNlciwgLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbikgJT4lIG11dGF0ZShyYW5rID0gLXJhbmspICU+JSAjIGNoYW5nZSBzaWduIG9mIHJhbmsgdG8gbWFrZSBpdCBpbmNyZWFzZSB3aXRoIHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzDQogIHJlbmFtZSggYXNzZXRzID0gbm9fb2ZfYXNzZXRzLCANCiAgICAgICAgICBwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgdG90YWwgPSB0b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSANCg0KDQpsaWJyYXJ5KGNvcnJwbG90KSAjIGNvcnJlbGF0aW9uIHBsb3RzDQojIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9jb3JycGxvdC92aWduZXR0ZXMvY29ycnBsb3QtaW50cm8uaHRtbA0KDQpjb3IgPC0gY29yKGRmKQ0KY29yX210ZXN0IDwtIGNvci5tdGVzdChkZiwgY29uZi5sZXZlbCA9IDAuOTkpICMgY29tYmluaW5nIGNvcnJlbG9ncmFtIHdpdGggc2lnbmlmaWNhbmNlIHRlc3QNCmNvcnJwbG90KGNvciwgbWV0aG9kID0gIm51bWJlciIsIG9yZGVyID0gJ2hjbHVzdCcsIGFkZHJlY3QgPSAzLCBwLm1hdCA9IGNvcl9tdGVzdCRwLCBpbnNpZyA9ICJwY2giKSAjIG9yZGVyID0gQU9FLCBGUEMsIGhjbHVzdCArIGFkZHJlY3QNCg0KY29ycnBsb3QoY29yLCBwLm1hdCA9IGNvcl9tdGVzdCRwLCBsb3cgPSBjb3JfbXRlc3QkbG93Q0ksIHVwcCA9IGNvcl9tdGVzdCR1cHBDSSwgb3JkZXIgPSAnaGNsdXN0Jywgc2lnLmxldmVsID0gMC4wMSwgdGwucG9zID0gJ2QnLCBhZGRyZWN0ID0gMywgcmVjdC5jb2wgPSAnbmF2eScsIHBsb3RDID0gJ3JlY3QnLCBjbC5wb3MgPSAnbicsIGluc2lnID0gInAtdmFsdWUiKQ0KYGBgDQoNCi0tLQ0KTG9yZW56IGN1cnZlICYgR2luaSBjb2VmZmljaWVudA0KLS0tDQphIEdpbmkgY29lZmZpY2llbnQgb2YgMC41NiBpcyBxdWl0ZSBoaWdoLCB3aGljaCBtZWFucyB0aGF0IG9ubHkgYSB0b3AgZmV3IHByb2R1Y2VycyBhcmUgcmVzcG9uc2libGUgZm9yIGEgbGFyZ2UgYW1vdW50IG9mIHRoZSB0b3RhbCBTVVAgd2FzdGUgY29udHJpYnV0aW9uLCBpbiBvdGhlciB3b3JkcyAxMCUgYXJlIHJlc3BvbnNpYmxlIGZvciA0NCUgb2YgdGhlIHdhc3RlDQoNCg0KYGBge3J9DQpuYW1lID0gYygndG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdQcm9kdWNlcnMnKQ0KZGYgPC0gcGxhc3RpYyAlPiUgcmVuYW1lKHZhbHVlID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkgJT4lIHNlbGVjdCh2YWx1ZSkgDQoNCmxpYnJhcnkoZ2dsb3JlbnopICN0cmFuc2Zvcm1hdGlvbnMgZm9yIHBsb3R0aW5nIExvcmVueiBjdXJ2ZSwgaHR0cHM6Ly9naXRodWIuY29tL2pqY2hlcm4vZ2dsb3JlbnoNCg0KbG9yZW56Y3VydmUgPC0gZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHZhbHVlKSkgKw0KICAgIHN0YXRfbG9yZW56KGRlc2MgPSBGQUxTRSkgKw0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGhyYnJ0aGVtZXM6OnNjYWxlX3hfcGVyY2VudCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV95X3BlcmNlbnQoKSArDQogICAgIyBocmJydGhlbWVzOjp0aGVtZV9pcHN1bV9yYygpICsNCiAgICBhbm5vdGF0ZV9pbmVxKGRmJHZhbHVlKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiTG9yZW56IGN1cnZlIGZvciIsIG5hbWVbMV0sIHNlcD0iICIpKSANCmxvcmVuemN1cnZlIDwtIGdncGxvdGx5KGxvcmVuemN1cnZlKSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlKCJjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YiLCBuYW1lWzFdLCBzZXA9IiAiKSksIHhheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlKCJjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YiLCBuYW1lWzJdLCBzZXA9IiAiKSkpIA0KDQpsb3JlbnpjdXJ2ZQ0KYGBgDQpgYGB7cn0NCm5hbWUgPSBjKCdmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdyaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZScsICdQcm9kdWNlcnMnLCAnZmxleGlibGUnLCAncmlnaWQnKQ0KZGYgPC0gcGxhc3RpYyAlPiUgcmVuYW1lKGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSBzZWxlY3QoZmxleGlibGUsIHJpZ2lkKSAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGZsZXhpYmxlLHJpZ2lkKSkNCg0KbGlicmFyeShnZ2xvcmVueikgI3RyYW5zZm9ybWF0aW9ucyBmb3IgcGxvdHRpbmcgTG9yZW56IGN1cnZlLCBodHRwczovL2dpdGh1Yi5jb20vampjaGVybi9nZ2xvcmVueg0KDQojIGdldCBnZ3Bsb3Qgc3RhbmRhcmQgY29sb3JzIGZvciBncm91cGluZywgd2hpY2ggYXJlIGVxdWFsbHkgc3BhY2VkIGh1ZXMgYXJvdW5kIHRoZSBjb2xvciB3aGVlbCwgc3RhcnRpbmcgZnJvbSAxNQ0KZ2dfY29sb3JfaHVlIDwtIGZ1bmN0aW9uKG4pIHsNCiAgaHVlcyA9IHNlcSgxNSwgMzc1LCBsZW5ndGggPSBuICsgMSkNCiAgaGNsKGggPSBodWVzLCBsID0gNjUsIGMgPSAxMDApWzE6bl0NCn0NCg0KbG9yZW56Y3VydmUgPC0gZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICAgIHN0YXRfbG9yZW56KGRlc2MgPSBGQUxTRSkgKw0KICAgIGNvb3JkX2ZpeGVkKCkgKw0KICAgIGdlb21fYWJsaW5lKGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGhyYnJ0aGVtZXM6OnNjYWxlX3hfcGVyY2VudCgpICsNCiAgICBocmJydGhlbWVzOjpzY2FsZV95X3BlcmNlbnQoKSArDQogICAgIyBocmJydGhlbWVzOjp0aGVtZV9pcHN1bV9yYygpICsNCiAgICBhbm5vdGF0ZV9pbmVxKGZpbHRlcihkZiwgbmFtZSA9PSBuYW1lWzRdKSR2YWx1ZSwgeSA9IDAuOTUsIGNvbG91ciA9IGdnX2NvbG9yX2h1ZSgyKVsxXSkgKw0KICAgIGFubm90YXRlX2luZXEoZmlsdGVyKGRmLCBuYW1lID09IG5hbWVbNV0pJHZhbHVlLCB5ID0gMC45MCwgY29sb3VyID0gZ2dfY29sb3JfaHVlKDIpWzJdKSArDQogICAgZ2d0aXRsZShwYXN0ZSgiY29tcGFyZSBMb3JlbnogY3VydmUgb2YiLCBuYW1lWzFdLCAiYW5kIiwgbmFtZVsyXSwgc2VwPSIgIikpIA0KbG9yZW56Y3VydmUgPC0gZ2dwbG90bHkobG9yZW56Y3VydmUpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHRpdGxlID0gcGFzdGUoImN1bXVsYXRpdmUgcGVyY2VudGFnZSBvZjxicj4iLCBuYW1lWzFdLCAiPGJyPiIsIG5hbWVbMl0sIHNlcD0iIikpLCB4YXhpcyA9IGxpc3QodGl0bGUgPSBwYXN0ZSgiY3VtdWxhdGl2ZSBwZXJjZW50YWdlIG9mIiwgbmFtZVsyXSwgc2VwPSIgIikpKSANCg0KbG9yZW56Y3VydmUNCmBgYA0KDQotLS0NCnByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgY29sb3JlZCBieSBzZWxmIG9yZ2FuaXppbmcgbWFwIGNsdXN0ZXINCi0tLQ0KSSBuZWVkIG1vcmUga25vd2xlZGdlIGhvdyB0byB3b3JrIHdpdGggYW5kIGludGVycHJldCBTT00gYW5kIFBDQSwgbWF5YmUgYWxzbyBub3QgZW5vdWdoIG9ic2VydmF0aW9ucyBpbiBkYXRhIHNldA0KaHR0cHM6Ly9pYW1jaWVyYS5naXRodWIuaW8vU09NZXhhbXBsZS9odG1sL1NPTV9STkFzZXFfdHV0b3JpYWxfcGFydDJhX1NPTS5odG1sDQoNCmJ1dCB0aGUgY2x1c3RlcmluZyBmcm9tIFNPTSwgd2hlbiBvbmx5IGluZGVwZW5kZW50IHZhcmlhYmxlcyBhcmUgdXNlZCwgYWN0dWFsbHkgc2hvdyB0aGUgdHdvIGFzc3VtZWQgc3VicG9wdWxhdGlvbnMgYmFzZWQgb24gcmlnaWQgdnMgZmxleGlibGUgcHJvZHVjaXRvbg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCiMgV2UgbmVlZCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmFzZWQgb24gc2NhbGUgZnVuY3Rpb24gYmVjYXVzZSB0aGUgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgc2NhbGVzOyBOb3JtYWxpemF0aW9uIG1lYW5zIHN1YnRyYWN0aW5nIG1lYW4gZnJvbSBlYWNoIG9ic2VydmF0aW9uIGFuZCBkaXZpZGluZyB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbi4gQ2hlY2sgYWxsIHRoZSB2YXJpYWJsZXMgbWVhbiB2YWx1ZXMgYXJlIHplcm8gbm93DQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0KaGVhZChzY2FsZV9kYXRhKQ0KDQoNCmxpYnJhcnkoa29ob25lbikgIyBmdW5jdGlvbnMgdG8gdHJhaW4gc2VsZi1vcmdhbmlzaW5nIG1hcHMgKFNPTXMpDQoNCiMgcHJpbmNpcGxlIGNvbXBvbmVudCBhbmFseXNpcw0KcGNhIDwtIHByY29tcChzY2FsZV9kYXRhLCBzY2FsZT1GQUxTRSkNCnN1bW1hcnkocGNhKQ0KDQojIHZpc3VhbGl6ZSBwY3MgcmVzdWx0cw0KIyBDb250cmlidXRpb25zIG9mIHZhcmlhYmxlcyB0byBQQzENCmZ2aXpfY29udHJpYihwY2EsIGNob2ljZSA9ICJ2YXIiLCBheGVzID0gMSwgdG9wID0gMTApDQojIENvbnRyaWJ1dGlvbnMgb2YgdmFyaWFibGVzIHRvIFBDMg0KZnZpel9jb250cmliKHBjYSwgY2hvaWNlID0gInZhciIsIGF4ZXMgPSAyLCB0b3AgPSAxMCkNCiMgQ29udHJvbCB2YXJpYWJsZSBjb2xvcnMgdXNpbmcgdGhlaXIgY29udHJpYnV0aW9ucyB0byB0aGUgcHJpbmNpcGxlIGF4aXMNCmZ2aXpfcGNhX3ZhcihwY2EsIGNvbC52YXI9ImNvbnRyaWIiLA0KICAgICAgICAgICAgIGdyYWRpZW50LmNvbHMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpLA0KICAgICAgICAgICAgIHJlcGVsID0gVFJVRSAjIEF2b2lkIHRleHQgb3ZlcmxhcHBpbmcNCiAgICAgICAgICAgICApICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiVmFyaWFibGVzIC0gUENBIikNCg0KIyBhZGQgYmFjayB0byBvcmlnaW5hbCBzbyBldmVyeXRoaW5nIGlzIHRvZ2V0aGVyDQpwY2Ffc2NvcmVzIDwtIGRhdGEuZnJhbWUocGNhJHgpDQpkYXRhX3ZhbCA8LSBjYmluZChkZiwgcGNhX3Njb3JlcykNCg0KcGNhX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdmFsLCBhZXMoeCA9IFBDMSwgeSA9IFBDMikpICsNCiAgICBnZW9tX3J1ZyhhbHBoYSA9IDAuNSkgKyAjIHR3byAxZCBtYXJnaW5hbCBkaXN0cmlidXRpb25zLCBkaXNwbGF5IGluZGl2aWR1YWwgY2FzZXMgc28gYXJlIGJlc3QgdXNlZCB3aXRoIHNtYWxsZXIgZGF0YXNldHMNCiAgICBnZW9tX2RlbnNpdHlfMmQoYWxwaGEgPSAwLjIsIGJpbnMgPSA0KSArIyAyRCBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIHVzaW5nIE1BU1M6OmtkZTJkKCkgYW5kIGRpc3BsYXkgdGhlIHJlc3VsdHMgd2l0aCBjb250b3Vycw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkNCnBjYV9wbG90IDwtIGdncGxvdGx5KHBjYV9wbG90KSAlPiUgbGF5b3V0KCkNCg0KcGNhX3Bsb3QNCg0KIyBjbHVzdGVyaW5nIGlzIHBlcmZvcm1lZCB1c2luZyB0aGUgc29tKCkgZnVuY3Rpb24gb24gdGhlIHNjYWxlZCBnZW5lIGV4cHJlc3Npb24gdmFsdWVzLg0Kc2V0LnNlZWQoMykNCg0KIyBkZWZpbmUgYSBncmlkIGZvciB0aGUgU09NIGFuZCB0cmFpbg0KZ3JpZF9zaXplIDwtIG5jb2woc2NhbGVfZGF0YSkNCnNvbV9ncmlkIDwtIHNvbWdyaWQoeGRpbSA9IGdyaWRfc2l6ZSwgeWRpbSA9IGdyaWRfc2l6ZSwgdG9wbyA9ICdoZXhhZ29uYWwnKQ0Kc29tX21vZGVsIDwtIHNvbShzY2FsZV9kYXRhLCBncmlkID0gc29tX2dyaWQpDQpzdW1tYXJ5KHNvbV9tb2RlbCkNCg0KIyBnZW5lcmF0ZSBzb20gcGxvdHMgYWZ0ZXIgdHJhaW5pbmcNCnBsb3Qoc29tX21vZGVsLCB0eXBlID0gJ21hcHBpbmcnKQ0KcGxvdChzb21fbW9kZWwsIHR5cGUgPSAnY29kZXMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdjb3VudHMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdkaXN0Lm5laWdoYm91cnMnKQ0KIyBwbG90KHNvbV9tb2RlbCwgdHlwZSA9ICdxdWFsaXR5JykNCiMgcGxvdChzb21fbW9kZWwsIHR5cGUgPSAnY2hhbmdlcycpDQoNCiMgZnVydGhlciBzcGxpdCB0aGUgY2x1c3RlcnMgaW50byBhIHNtYWxsZXIgc2V0IG9mIGNsdXN0ZXJzIHVzaW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLg0Kc29tX2NsdXN0ZXIgPC0gY3V0cmVlKGhjbHVzdChkaXN0KHNvbV9tb2RlbCRjb2Rlc1tbMV1dKSksIDIpICMgdXNlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRvIGNsdXN0ZXIgdGhlIGNvZGVib29rIHZlY3RvcnMNCg0KcGxvdChzb21fbW9kZWwsIHR5cGU9Im1hcHBpbmciLCBiZ2NvbCA9IHNvbV9jbHVzdGVyLCBtYWluID0gIkNsdXN0ZXJzIikNCmFkZC5jbHVzdGVyLmJvdW5kYXJpZXMoc29tX21vZGVsLCBzb21fY2x1c3RlcikNCg0KIyBhdHRhY2ggdGhlIGhpZXJjaGFsIGNsdXN0ZXIgdG8gdGhlIGxhcmdlciBkYXRhc2V0IGRhdGFfdmFsLg0KZ3JpZFNxdWFyZSA8LSBncmlkX3NpemUgKiBncmlkX3NpemUNCnNvbV9jbHVzdGVyS2V5IDwtIGRhdGEuZnJhbWUoc29tX2NsdXN0ZXIpDQpzb21fY2x1c3RlcktleSR1bml0X2NsYXNzaWYgPC0gYygxOmdyaWRTcXVhcmUpDQpkYXRhX3ZhbCA8LSBjYmluZChkYXRhX3ZhbCxzb21fbW9kZWwkdW5pdC5jbGFzc2lmLHNvbV9tb2RlbCRkaXN0YW5jZXMpICU+JSByZW5hbWUodW5pdF9jbGFzc2lmID0gJ3NvbV9tb2RlbCR1bml0LmNsYXNzaWYnLCBkaXN0YW5jZXMgPSAnc29tX21vZGVsJGRpc3RhbmNlcycpDQpkYXRhX3ZhbCA8LSBtZXJnZShkYXRhX3ZhbCwgc29tX2NsdXN0ZXJLZXksIGJ5LnggPSAidW5pdF9jbGFzc2lmIiApDQpoZWFkKGRhdGFfdmFsKQ0KDQojIHBsb3QgcGNhIHdpdGggY29sb3JlZCBjbHVzdGVycw0KcGNhc29tX3Bsb3QgPC0gZ2dwbG90KGRhdGFfdmFsLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBmYWN0b3Ioc29tX2NsdXN0ZXIpLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikpICsNCiAgICBnZW9tX3J1ZyhhbHBoYSA9IDAuNSkgKyAjIHR3byAxZCBtYXJnaW5hbCBkaXN0cmlidXRpb25zLCBkaXNwbGF5IGluZGl2aWR1YWwgY2FzZXMgc28gYXJlIGJlc3QgdXNlZCB3aXRoIHNtYWxsZXIgZGF0YXNldHMNCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpDQpwY2Fzb21fcGxvdCA8LSBnZ3Bsb3RseShwY2Fzb21fcGxvdCkgJT4lIGxheW91dCgpDQoNCnBjYXNvbV9wbG90DQpgYGANCg0KYGBge3J9DQojIHR3byB2YXJpYWJsZXMsIGNvbnRpbnVvdXMgeCwgY29udGludW91cyB5LCBzaG93IHRyZW5kIGFuZCBkaXN0cmlidXRpb24NCm5hbWUgPSBjKCdwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzJywgJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnKQ0KZGYgPC0gbWVyZ2UocGxhc3RpYywgZGF0YV92YWwsIGJ5LnggPSAncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBkZiAlPiUgcmVuYW1lKHggPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCB5ID0gdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgY2x1c3RlciA9IHNvbV9jbHVzdGVyLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikgJT4lIHNlbGVjdCh4LCB5LCBjbHVzdGVyLCB0ZXh0KSANCg0KIyBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbA0KcG9pbnRfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArDQogICAgZ2VvbV9ydWcoYWxwaGEgPSAwLjUpICsgIyB0d28gMWQgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucywgZGlzcGxheSBpbmRpdmlkdWFsIGNhc2VzIHNvIGFyZSBiZXN0IHVzZWQgd2l0aCBzbWFsbGVyIGRhdGFzZXRzDQogICAgZ2VvbV9kZW5zaXR5XzJkKGFscGhhID0gMC4yLCBiaW5zID0gNCkgKyMgMkQga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiB1c2luZyBNQVNTOjprZGUyZCgpIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzIHdpdGggY29udG91cnMNCiAgICBnZW9tX3Ntb290aChmaWxsID0gImdyZXk5MCIpICsgIyBhaWRzIHRoZSBleWUgaW4gc2VlaW5nIHBhdHRlcm5zIGluIHRoZSBwcmVzZW5jZSBvZiBvdmVycGxvdHRpbmcNCiAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gdGV4dCksIGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJ0cmVuZCBvZiIsIG5hbWVbMl0sICJvdmVyIiwgbmFtZVsxXSwgc2VwPSIgIikpIA0KcG9pbnRfcGxvdCA8LSBnZ3Bsb3RseShwb2ludF9wbG90KSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdChzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KeF9kZW5zaXR5X3Bsb3QgPC0gZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIikgKyAjIGRyYXdzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQ0KICAgICMgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeF9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeF9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCnlfZGVuc2l0eV9wbG90IDwtIGRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCBjb2xvciA9IGZhY3RvcihjbHVzdGVyKSkpICsNCiAgICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIpICsgIyBkcmF3cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSwgd2hpY2ggaXMgYSBzbW9vdGhlZCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0NCiAgICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeV9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeV9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcXVhbnRpbGUuaHRtbA0KcXVhbHRpbGVfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIGdlb21fcXVhbnRpbGUoYWxwaGEgPSAwLjgpICsgIyBmaXRzIGEgcXVhbnRpbGUgcmVncmVzc2lvbiB0byB0aGUgZGF0YSBhbmQgZHJhd3MgdGhlIGZpdHRlZCBxdWFudGlsZXMgd2l0aCBsaW5lcw0KICAgIHRoZW1lX21pbmltYWwoKSANCnF1YWx0aWxlX3Bsb3QgPC0gZ2dwbG90bHkocXVhbHRpbGVfcGxvdCkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgbWVyZ2UgZmlndXJlcyBpbnRvIG9uZSBwbG90LCB2aWEgc3VicGxvdHMsIGh0dHBzOi8vcGxvdGx5LXIuY29tL2FycmFuZ2luZy12aWV3cy5odG1sDQpzdWIxIDwtIHN1YnBsb3QoeF9kZW5zaXR5X3Bsb3QsIHBsb3RseV9lbXB0eSgpLCBwb2ludF9wbG90LCB5X2RlbnNpdHlfcGxvdCwgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLCBoZWlnaHRzID0gYygwLjE1LCAwLjg1KSwgd2lkdGhzID0gYygwLjksIDAuMSksIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkgJT4lIGxheW91dCgpDQpzdWIyIDwtIHN1YnBsb3QocXVhbHRpbGVfcGxvdCwgcGxvdGx5X2VtcHR5KCksIG1hcmdpbiA9IDAsIHdpZHRocyA9IGMoMC45LCAwLjEwKSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKSAlPiUgbGF5b3V0KCkNCmZpZyA8LSBzdWJwbG90KHN1YjEsIHN1YjIsIG5yb3dzID0gMiwgbWFyZ2luID0gMCwgaGVpZ2h0cyA9IGMoMC44LCAwLjIpLCBzaGFyZVggPSBUUlVFKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9IG5hbWVbMV0pLCB5YXhpcyA9IGxpc3QodGl0bGUgPSBuYW1lWzJdKSkNCiAgDQpmaWcNCmBgYA0KDQotLS0NCnBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdA0KLS0tDQpwYXJhbGxlbCBjb29yZGluYXRlIHBsb3QgaXMgbm90IHZlcnkgaW5zaWdodGZ1bCwgc2luY2UgdGhlcmUgaXMgc28gbXVjaCBkYXRhIGNsb3NlIHRvZ2V0aGVyLCB3ZSBjYW5ub3Qgc2VlIGFueSBncm91cGluZyB3aXRob3V0IHByaW9yIGtub3dsZWRnZSwgb25seSBoaW50IGlzIHdoZW4geW91IHpvb20gaW4gYXQgdGhlIGJ1bGsgb2YgdGhlIGRhdGEgaW4gdGhlIGxvd2VyIHRoaXJkICh5IGRpcmVjdGlvbiksIHRoZXJlIGlzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24NCg0KYGBge3J9DQpuYW1lID0gYygncGxhc3RpYyBwcm9kdWNlcnMgY2x1c3RlcmVkIGJ5IGZvY3VzJykNCmRmIDwtIG1lcmdlKHBsYXN0aWMsIHNlbGVjdChkYXRhX3ZhbCwgc29tX2NsdXN0ZXIsIHBvbHltZXJfcHJvZHVjZXIpLCBieS54ID0gJ3BvbHltZXJfcHJvZHVjZXInKSAlPiUNCiAgbXV0YXRlKHNvbV9jbHVzdGVyID0gYXMuY2hhcmFjdGVyKHNvbV9jbHVzdGVyKSkgJT4lDQogIHJlbmFtZShwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwNCiAgICAgICAgICBhc3NldHMgPSAgbm9fb2ZfYXNzZXRzLA0KICAgICAgICAgIGNsdXN0ZXIgPSBzb21fY2x1c3RlcikgJT4lIA0KICBzZWxlY3QoYXNzZXRzLCBwcm9kdWN0LCBmbGV4aWJsZSwgcmlnaWQsIGNsdXN0ZXIpIA0KDQpsaWJyYXJ5KEdHYWxseSkgIyBleHRlbmRzIGdncGxvdDIgYnkgYWRkaW5nIHNldmVyYWwgZnVuY3Rpb25zIHRvIHJlZHVjZSB0aGUgY29tcGxleGl0eSBvZiBjb21iaW5pbmcgZ2VvbXMgd2l0aCB0cmFuc2Zvcm1lZCBkYXRhDQoNCiMgaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS9wYXJhbGxlbC1wbG90LWdnYWxseS5odG1sI2N1c3RvbQ0KcGFyY29vcmRfcGxvdCA8LSBnZ3BhcmNvb3JkKGRmLA0KICAgICAgICAgICBjb2x1bW5zID0gMTo0LCBncm91cENvbHVtbiA9IDUsDQogICAgICAgICAgIHNjYWxlPSdjZW50ZXInLCAjIHNjYWxpbmc6IHN0YW5kYXJkaXplIGFuZCBjZW50ZXIgdmFyaWFibGVzDQogICAgICAgICAgIHNob3dQb2ludHMgPSBUUlVFLA0KICAgICAgICAgICB0aXRsZSA9IG5hbWUsDQogICAgICAgICAgIGFscGhhTGluZXMgPSAwLjMpICsNCiAgdGhlbWVfbWluaW1hbCgpIA0KcGFyY29vcmRfcGxvdCA8LSBnZ3Bsb3RseShwYXJjb29yZF9wbG90KSAlPiUgbGF5b3V0KGF1dG9zaXplPVQpDQoNCnBhcmNvb3JkX3Bsb3QNCmBgYA0KDQotLS0NCmstbWVhbnMgY2x1c3RlcmluZw0KLS0tDQpodHRwczovL3N0YXRzYW5kci5jb20vYmxvZy9jbHVzdGVyaW5nLWFuYWx5c2lzLWstbWVhbnMtYW5kLWhpZXJhcmNoaWNhbC1jbHVzdGVyaW5nLWJ5LWhhbmQtYW5kLWluLXIvI29wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzDQpBcyBhIHJlbWluZGVyLCB0aGlzIG1ldGhvZCBhaW1zIGF0IHBhcnRpdGlvbmluZyBuIG9ic2VydmF0aW9ucyBpbnRvIGsgY2x1c3RlcnMgaW4gd2hpY2ggZWFjaCBvYnNlcnZhdGlvbiBiZWxvbmdzIHRvIHRoZSBjbHVzdGVyIHdpdGggdGhlIGNsb3Nlc3QgYXZlcmFnZSwgc2VydmluZyBhcyBhIHByb3RvdHlwZSBvZiB0aGUgY2x1c3Rlcg0Kd29ya3Mgbm90IHF1aXRlIGFzIHdlbGwgYXMgU09NLCBidXQgdmVyeSBjbG9zZSwgYWxzbyByZWNvbW1lbmQgY2x1c3RlciBpcyB0aHJlZSwgYnV0IGNsb3NlbHkgZm9sbG93ZWQgYnkgMiwgc2luY2UgMyBkb2VzIG5vdCByZXZlYWwgYW55IHNpZ25pZmljYW50IGNvbm5lY3Rpb24gcmlnaHQgbm93LCBJIHdvbmRlciB3aGF0IHRoYXQgc2hvd3MNCg0KYGBge3J9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQpsaWJyYXJ5KE5iQ2x1c3QpICMgZGV0ZXJtaW5pbmcgdGhlIG9wdGltYWwgbnVtYmVyIG9mIGNsdXN0ZXJzIGluIGEgZGF0YSBzZXQNCmxpYnJhcnkoY2x1c3RlcikgIyBjb21wdXRlcyBhZ2dsb21lcmF0aXZlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIG9mIHRoZSBkYXRhc2V0DQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCmhlYWQoc2NhbGVfZGF0YSkNCg0Ka21lYW5zX21vZGVsIDwtIGttZWFucyhzY2FsZV9kYXRhLCBjZW50ZXJzID0gMiwgbnN0YXJ0ID0gMTIpICMgIGstbWVhbnMgY2x1c3RlcmluZyBpcyBkb25lIHZpYSB0aGUga21lYW5zKCkgZnVuY3Rpb24sIHdpdGggdGhlIGFyZ3VtZW50IGNlbnRlcnMgdGhhdCBjb3JyZXNwb25kcyB0byB0aGUgbnVtYmVyIG9mIGRlc2lyZWQgY2x1c3RlcnMNCmRmX2NsdXN0ZXIgPC0gdGliYmxlKGRmLCBjbHVzdGVyID0gYXMuZmFjdG9yKGttZWFuc19tb2RlbCRjbHVzdGVyKSkgIyBzdG9yZSBjbHVzdGVyIGluIG9yaWdpbmFsIGRhdGEgc2V0IGFzIGNvbHVtbg0KaGVhZChkZl9jbHVzdGVyKQ0KDQojIGNoZWNrIHF1YWxpdHkgb2YgYSBrLW1lYW5zIHBhcnRpdGlvbg0KcXVhbGl0eSA8LSBrbWVhbnNfbW9kZWwkYmV0d2VlbnNzIC8ga21lYW5zX21vZGVsJHRvdHNzIA0KcHJpbnQocGFzdGUoInF1YWxpdHkgb2Yga21lYW5zIGlzIEJTUy9UU1M6ICIsIGZvcm1hdChyb3VuZChxdWFsaXR5LDIpLCBuc21hbGwgPSAyKSkpDQoNCiMgZmluZCBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KHNjYWxlX2RhdGEsIGttZWFucywgbWV0aG9kID0gJ3dzcycpICsgIyBFbGJvdyBtZXRob2QsIG5lZWRzIHNjYWxlZCBkYXRhDQogICMgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMiwgbGluZXR5cGUgPSAyKSArICMgYWRkIGxpbmUgZm9yIGJldHRlciB2aXN1YWxpemF0aW9uDQogIGxhYnMoc3VidGl0bGUgPSAiRWxib3cgbWV0aG9kIikgIyBhZGQgc3VidGl0bGUNCg0KZnZpel9uYmNsdXN0KHNjYWxlX2RhdGEsIGttZWFucywgbWV0aG9kID0gJ3NpbGhvdWV0dGUnKSArICMgU2lsaG91ZXR0ZSBtZXRob2QsIG5lZWRzIHNjYWxlZCBkYXRhDQogIGxhYnMoc3VidGl0bGUgPSAiU2lsaG91ZXR0ZSBtZXRob2QiKSAjIGFkZCBzdWJ0aXRsZQ0KDQpmdml6X25iY2x1c3QoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0sIGttZWFucywgIyBHYXAgc3RhdGlzdGljcywgbmVlZHMgb3JpZ2luYWwgZGF0YSA/DQogICAgICAgICAgICAgbnN0YXJ0ID0gMjUsDQogICAgICAgICAgICAgbWV0aG9kID0gJ2dhcF9zdGF0JywNCiAgICAgICAgICAgICBuYm9vdCA9IDEwMCkgKyAjIHJlZHVjZSBpdCBmb3IgbG93ZXIgY29tcHV0YXRpb24gdGltZSwgYnV0IGxlc3MgcHJlY2lzZSByZXN1bHRzDQogIGxhYnMoc3VidGl0bGUgPSAiR2FwIHN0YXRpc3RpY3MgbWV0aG9kIikNCg0KbmJjbHVzdF9vdXQgPC0gTmJDbHVzdChkYXRhID0gZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0sICMgTmJDbHVzdCBuZWVkcyB0aGUgb3JpZ2luYWwgZGF0YSA/DQogICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gJ2V1Y2xpZGVhbicsDQogICAgICAgICAgICAgICAgICAgICAgIG1pbi5uYyA9IDIsICMgbWluaW11bSBudW1iZXIgb2YgY2x1c3RlcnMNCiAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm5jID0gMTAsICMgbWF4aW11bSBudW1iZXIgb2YgY2x1c3Rlcg0KICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAnY29tcGxldGUnLA0KICAgICAgICAgICAgICAgICAgICAgICBpbmRleCA9ICdhbGwnKQ0KZnZpel9uYmNsdXN0KG5iY2x1c3Rfb3V0KSArIHRoZW1lX21pbmltYWwoKSArDQogIGxhYnMoc3VidGl0bGUgPSAiTmJDbHVzdCByZXN1bHRzIikNCg0KIyBjaGVjayBxdWFsaXR5IG9mIGNsdXN0ZXJpbmcNCiMgaWYgYSBsYXJnZSBtYWpvcml0eSBvZiB0aGUgc2lsaG91ZXR0ZSBjb2VmZmljaWVudHMgYXJlIHBvc2l0aXZlLCBpdCBpbmRpY2F0ZXMgdGhhdCB0aGUgb2JzZXJ2YXRpb25zIGFyZSBwbGFjZWQgaW4gdGhlIGNvcnJlY3QgZ3JvdXANCnNpbCA8LSBzaWxob3VldHRlKGttZWFuc19tb2RlbCRjbHVzdGVyLCBkaXN0KHNjYWxlX2RhdGEpKSANCmZ2aXpfc2lsaG91ZXR0ZShzaWwpDQoNCmZ2aXpfY2x1c3RlcihrbWVhbnNfbW9kZWwsIGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdLCBlbGxpcHNlLnR5cGUgPSAnbm9ybScpICsgdGhlbWVfbWluaW1hbCgpDQpmdml6X2NsdXN0ZXIoa21lYW5zX21vZGVsLCBkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgKyB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0Ka21lYW5fY2FsYyA8LSBmdW5jdGlvbihkZiwgLi4uKXsNCiAga21lYW5zKGRmLCBzY2FsZWQgPSAuLi4sIG5zdGFydCA9IDMwKQ0KfQ0KDQprbTIgPC0ga21lYW5fY2FsYyhzY2FsZV9kYXRhLCAyKQ0Ka20zIDwtIGttZWFuX2NhbGMoc2NhbGVfZGF0YSwgMykNCmttNCA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNCkNCmttNSA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNSkNCmttNiA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNikNCmttNyA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgNykNCg0KcDEgPC0gZnZpel9jbHVzdGVyKGttMiwgZGF0YSA9IHNjYWxlX2RhdGEsIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiKSArIHRoZW1lX21pbmltYWwoKQ0KcDEgPC0gZ2dwbG90bHkocDEpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAiayA9IDIiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCnAyIDwtIGZ2aXpfY2x1c3RlcihrbTMsIGRhdGEgPSBzY2FsZV9kYXRhLCBlbGxpcHNlLnR5cGUgPSAiY29udmV4IikgKyB0aGVtZV9taW5pbWFsKCkNCnAyIDwtIGdncGxvdGx5KHAyKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImsgPSAzIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQpwMyA8LSBmdml6X2NsdXN0ZXIoa200LCBkYXRhID0gc2NhbGVfZGF0YSwgZWxsaXBzZS50eXBlID0gImNvbnZleCIpICsgdGhlbWVfbWluaW1hbCgpDQpwMyA8LSBnZ3Bsb3RseShwMykgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJrID0gNCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KcDQgPC0gZnZpel9jbHVzdGVyKGttNSwgZGF0YSA9IHNjYWxlX2RhdGEsIGVsbGlwc2UudHlwZSA9ICJjb252ZXgiKSArIHRoZW1lX21pbmltYWwoKQ0KcDQgPC0gZ2dwbG90bHkocDQpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAiayA9IDUiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCnA1IDwtIGZ2aXpfY2x1c3RlcihrbTYsIGRhdGEgPSBzY2FsZV9kYXRhLCBlbGxpcHNlLnR5cGUgPSAiY29udmV4IikgKyB0aGVtZV9taW5pbWFsKCkNCnA1IDwtIGdncGxvdGx5KHA1KSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImsgPSA2IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQpwNiA8LSBmdml6X2NsdXN0ZXIoa203LCBkYXRhID0gc2NhbGVfZGF0YSwgZWxsaXBzZS50eXBlID0gImNvbnZleCIpICsgdGhlbWVfbWluaW1hbCgpDQpwNiA8LSBnZ3Bsb3RseShwNikgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJrID0gNyIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpmaWcgPC0gc3VicGxvdChwMSwgcDIsIHAzICwgcDQsIHA1LCBwNiwgbnJvd3MgPSAyLCBzaGFyZVggPSBUUlVFLCBzaGFyZVkgPSBUUlVFKSAlPiUgbGF5b3V0KCkgIyBUT09EOiBtYWtlIGFsbCBwbG90cyBsaW5rZWQNCmZpZw0KYGBgDQoNCg0KYGBge3J9DQojIHR3byB2YXJpYWJsZXMsIGNvbnRpbnVvdXMgeCwgY29udGludW91cyB5LCBzaG93IHRyZW5kIGFuZCBkaXN0cmlidXRpb24NCm5hbWUgPSBjKCdwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzJywgJ3RvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUnKQ0KZGYgPC0gdGliYmxlKHBsYXN0aWMsIGNsdXN0ZXIgPSBhcy5mYWN0b3Ioa21lYW5zX21vZGVsJGNsdXN0ZXIpKQ0KZGYgPC0gZGYgJT4lIHJlbmFtZSh4ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgeSA9IHRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIGNsdXN0ZXIgPSBjbHVzdGVyLCB0ZXh0ID0gcG9seW1lcl9wcm9kdWNlcikgJT4lIHNlbGVjdCh4LCB5LCBjbHVzdGVyLCB0ZXh0KSANCg0KIyBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9zbW9vdGguaHRtbA0KcG9pbnRfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgICMgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjUsIHNpemUgPSAxKSArDQogICAgZ2VvbV9ydWcoYWxwaGEgPSAwLjUpICsgIyB0d28gMWQgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucywgZGlzcGxheSBpbmRpdmlkdWFsIGNhc2VzIHNvIGFyZSBiZXN0IHVzZWQgd2l0aCBzbWFsbGVyIGRhdGFzZXRzDQogICAgZ2VvbV9kZW5zaXR5XzJkKGFscGhhID0gMC4yLCBiaW5zID0gNCkgKyMgMkQga2VybmVsIGRlbnNpdHkgZXN0aW1hdGlvbiB1c2luZyBNQVNTOjprZGUyZCgpIGFuZCBkaXNwbGF5IHRoZSByZXN1bHRzIHdpdGggY29udG91cnMNCiAgICBnZW9tX3Ntb290aChmaWxsID0gImdyZXk5MCIpICsgIyBhaWRzIHRoZSBleWUgaW4gc2VlaW5nIHBhdHRlcm5zIGluIHRoZSBwcmVzZW5jZSBvZiBvdmVycGxvdHRpbmcNCiAgICBnZW9tX3BvaW50KGFlcyh0ZXh0ID0gdGV4dCksIGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpICsNCiAgICBnZ3RpdGxlKHBhc3RlKCJ0cmVuZCBvZiIsIG5hbWVbMl0sICJvdmVyIiwgbmFtZVsxXSwgc2VwPSIgIikpIA0KcG9pbnRfcGxvdCA8LSBnZ3Bsb3RseShwb2ludF9wbG90KSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdChzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KeF9kZW5zaXR5X3Bsb3QgPC0gZGYgJT4lDQogIGdncGxvdChhZXMoeCA9IHgsIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIHN0YXRfZGVuc2l0eShnZW9tPSJsaW5lIikgKyAjIGRyYXdzIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlLCB3aGljaCBpcyBhIHNtb290aGVkIHZlcnNpb24gb2YgdGhlIGhpc3RvZ3JhbQ0KICAgICMgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeF9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeF9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCnlfZGVuc2l0eV9wbG90IDwtIGRmICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5LCBjb2xvciA9IGZhY3RvcihjbHVzdGVyKSkpICsNCiAgICBzdGF0X2RlbnNpdHkoZ2VvbT0ibGluZSIpICsgIyBkcmF3cyBrZXJuZWwgZGVuc2l0eSBlc3RpbWF0ZSwgd2hpY2ggaXMgYSBzbW9vdGhlZCB2ZXJzaW9uIG9mIHRoZSBoaXN0b2dyYW0NCiAgICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KeV9kZW5zaXR5X3Bsb3QgPC0gZ2dwbG90bHkoeV9kZW5zaXR5X3Bsb3QpICU+JSBsYXlvdXQoeWF4aXMgPSBsaXN0KHNob3d0aWNrbGFiZWxzID0gRkFMU0UsIHNob3dncmlkID0gRkFMU0UpLCB4YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fcXVhbnRpbGUuaHRtbA0KcXVhbHRpbGVfcGxvdCA8LSBkZiAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0gZmFjdG9yKGNsdXN0ZXIpKSkgKw0KICAgIGdlb21fcXVhbnRpbGUoYWxwaGEgPSAwLjgpICsgIyBmaXRzIGEgcXVhbnRpbGUgcmVncmVzc2lvbiB0byB0aGUgZGF0YSBhbmQgZHJhd3MgdGhlIGZpdHRlZCBxdWFudGlsZXMgd2l0aCBsaW5lcw0KICAgIHRoZW1lX21pbmltYWwoKSANCnF1YWx0aWxlX3Bsb3QgPC0gZ2dwbG90bHkocXVhbHRpbGVfcGxvdCkgJT4lIGxheW91dCh5YXhpcyA9IGxpc3Qoc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSwgc2hvd2dyaWQgPSBGQUxTRSkpDQoNCiMgbWVyZ2UgZmlndXJlcyBpbnRvIG9uZSBwbG90LCB2aWEgc3VicGxvdHMsIGh0dHBzOi8vcGxvdGx5LXIuY29tL2FycmFuZ2luZy12aWV3cy5odG1sDQpzdWIxIDwtIHN1YnBsb3QoeF9kZW5zaXR5X3Bsb3QsIHBsb3RseV9lbXB0eSgpLCBwb2ludF9wbG90LCB5X2RlbnNpdHlfcGxvdCwgbnJvd3MgPSAyLCBtYXJnaW4gPSAwLCBoZWlnaHRzID0gYygwLjE1LCAwLjg1KSwgd2lkdGhzID0gYygwLjksIDAuMSksIHNoYXJlWCA9IFRSVUUsIHNoYXJlWSA9IFRSVUUsIHRpdGxlWCA9IEZBTFNFLCB0aXRsZVkgPSBGQUxTRSkgJT4lIGxheW91dCgpDQpzdWIyIDwtIHN1YnBsb3QocXVhbHRpbGVfcGxvdCwgcGxvdGx5X2VtcHR5KCksIG1hcmdpbiA9IDAsIHdpZHRocyA9IGMoMC45LCAwLjEwKSwgdGl0bGVYID0gRkFMU0UsIHRpdGxlWSA9IEZBTFNFKSAlPiUgbGF5b3V0KCkNCmZpZyA8LSBzdWJwbG90KHN1YjEsIHN1YjIsIG5yb3dzID0gMiwgbWFyZ2luID0gMCwgaGVpZ2h0cyA9IGMoMC44LCAwLjIpLCBzaGFyZVggPSBUUlVFKSAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh0aXRsZSA9IG5hbWVbMV0pLCB5YXhpcyA9IGxpc3QodGl0bGUgPSBuYW1lWzJdKSkNCg0KZmlnDQpgYGANCg0KLS0tDQpoaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KLS0tDQpodHRwczovL3N0YXRzYW5kci5jb20vYmxvZy9jbHVzdGVyaW5nLWFuYWx5c2lzLWstbWVhbnMtYW5kLWhpZXJhcmNoaWNhbC1jbHVzdGVyaW5nLWJ5LWhhbmQtYW5kLWluLXIvI29wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzDQpyZW1pbmQgdGhhdCB0aGUgZGlmZmVyZW5jZSB3aXRoIHRoZSBwYXJ0aXRpb24gYnkgay1tZWFucyBpcyB0aGF0IGZvciBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywgdGhlIG51bWJlciBvZiBjbGFzc2VzIGlzIG5vdCBzcGVjaWZpZWQgaW4gYWR2YW5jZQ0KDQppdCBzZWVtcyB0aGF0IG1vc3QgY2x1c3RlcnMgYXJlIGNvbmZ1c2VkIGJ5IHNvbWUgc3BlY2lmaWMgdmFsdWVzICg4MSwgNjMsIDcyLCAzNj8pLCBhcmUgdGhleSBzb21lIHNvcnQgb2Ygb3V0bGllciwgb3Igd2hhdCBpcyBzcGVjaWFsIGFib3V0IHRoZW0/DQoNCmBgYHtyfQ0KbmFtZSA9IGMoJ3BvbHltZXJfcHJvZHVjZXInKQ0KZGYgPC0gcGxhc3RpYyAlPiUgc2VsZWN0KC0gdG90YWxfd2FzdGVfZGl2X3Byb2R1Y3Rpb24sIC1yYW5rLCAtbm9fb2ZfYXNzZXRzLCAtdG90YWxfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkgJT4lICMgcmVtb3ZlZCB2YXJpYWJsZXMgd2hpY2ggYXJlIGRlcGVuZGVkIG9uIGVhY2ggb3RoZXINCiAgcmVuYW1lKCBwcm9kdWN0ID0gcHJvZHVjdGlvbl9vZl9pbl9zY29wZV9wb2x5bWVycywgDQogICAgICAgICAgZmxleGlibGUgPSBmbGV4aWJsZV9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSwgDQogICAgICAgICAgcmlnaWQgPSByaWdpZF9mb3JtYXRfY29udHJpYnV0aW9uX3RvX3N1cF93YXN0ZSkNCg0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeCh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHNjYWxlKGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkNCnNjYWxlX2RhdGEgPC0gYXMubWF0cml4KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSAjIHdpdGhvdXQgc2NhbGluZyBzaW5jZSB0aGUgdmFyaWFibGVzIGFjdHVhbGx5IGFyZSBvbiB0aGUgc2FtZSBzY2FsZQ0KaGVhZChzY2FsZV9kYXRhKQ0KDQpub19rID0gMjsNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogc2luZ2xlIGxpbmthZ2UNCmhjbHVzdF9yZXMgPC0gaGNsdXN0KGRpc3Qoc2NhbGVfZGF0YSksIG1ldGhvZCA9ICdzaW5nbGUnKQ0KZnZpel9kZW5kKGhjbHVzdF9yZXMsIGsgPSBub19rLCByZWN0ID0gVFJVRSkNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogY29tcGxldGUgbGlua2FnZQ0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2NvbXBsZXRlJykNCnBsb3QoaGNsdXN0X3JlcykNCnJlY3QuaGNsdXN0KGhjbHVzdF9yZXMsIGsgPSBub19rLCBib3JkZXIgPSAnYmx1ZScpDQoNCiMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmc6IGF2ZXJhZ2UgbGlua2FnZQ0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2F2ZXJhZ2UnKQ0KcGxvdChoY2x1c3RfcmVzKQ0KcmVjdC5oY2x1c3QoaGNsdXN0X3JlcywgayA9IG5vX2ssIGJvcmRlciA9ICdibHVlJykNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogd2FyZA0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ3dhcmQuRDInKQ0KZnZpel9kZW5kKGhjbHVzdF9yZXMsIGsgPSBub19rLCByZWN0ID0gVFJVRSkNCg0KIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZzogbWNxdWl0dHkNCmhjbHVzdF9yZXMgPC0gaGNsdXN0KGRpc3Qoc2NhbGVfZGF0YSksIG1ldGhvZCA9ICdtY3F1aXR0eScpDQpwbG90KGhjbHVzdF9yZXMpDQpyZWN0LmhjbHVzdChoY2x1c3RfcmVzLCBrID0gbm9faywgYm9yZGVyID0gJ2JsdWUnKQ0KDQojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiBjZW50cm9pZA0KaGNsdXN0X3JlcyA8LSBoY2x1c3QoZGlzdChzY2FsZV9kYXRhKSwgbWV0aG9kID0gJ2NlbnRyb2lkJykNCnBsb3QoaGNsdXN0X3JlcykNCnJlY3QuaGNsdXN0KGhjbHVzdF9yZXMsIGsgPSBub19rLCBib3JkZXIgPSAnYmx1ZScpDQpgYGANCg0KLS0tDQpDbHVzdHJlZQ0KLS0tDQpodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vMTAtdGlwcy1mb3ItY2hvb3NpbmctdGhlLW9wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzLTI3N2U5M2Q3MmQ5Mg0KSW4gdGhpcyBmaWd1cmUgdGhlIHNpemUgb2YgZWFjaCBub2RlIGNvcnJlc3BvbmRzIHRvIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpbiBlYWNoIGNsdXN0ZXIsIGFuZCB0aGUgYXJyb3dzIGFyZSBjb2xvdXJlZCBhY2NvcmRpbmcgdG8gdGhlIG51bWJlciBvZiBzYW1wbGVzIGVhY2ggY2x1c3RlciByZWNlaXZlcy4gQSBzZXBhcmF0ZSBzZXQgb2YgYXJyb3dzLCB0aGUgdHJhbnNwYXJlbnQgb25lcywgY2FsbGVkIHRoZSBpbmNvbWluZyBub2RlIHByb3BvcnRpb24sIGFyZSBhbHNvIGNvbG91cmVkIGFuZCBzaG93cyBob3cgc2FtcGxlcyBmcm9tIG9uZSBncm91cCBlbmQgdXAgaW4gYW5vdGhlciBncm91cCDigJQgYW4gaW5kaWNhdG9yIG9mIGNsdXN0ZXIgaW5zdGFiaWxpdHkuDQoNCmBgYHtyLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDl9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQojIGNvbmZpZw0Kbm9fb2ZfY2x1c3RlciA9IDkNCg0KbGlicmFyeShjbHVzdHJlZSkgIyBwcm9kdWNlIGNsdXN0ZXJpbmcgdHJlZXMsIGEgdmlzdWFsaXphdGlvbiBmb3IgaW50ZXJyb2dhdGluZyBjbHVzdGVyaW5ncyBhcyByZXNvbHV0aW9uIGluY3JlYXNlcw0KDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHQoc2NhbGUodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpKSkNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0Kc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pICMgd2l0aG91dCBzY2FsaW5nIHNpbmNlIHRoZSB2YXJpYWJsZXMgYWN0dWFsbHkgYXJlIG9uIHRoZSBzYW1lIHNjYWxlDQpoZWFkKHNjYWxlX2RhdGEpDQoNCnRtcCA8LSBOVUxMDQpmb3IgKGsgaW4gMTpub19vZl9jbHVzdGVyKXsNCiAgdG1wW2tdIDwtIGttZWFucyhzY2FsZV9kYXRhLCBrLCBuc3RhcnQgPSAzMCkNCn0NCg0KdG1wX2RmIDwtIGRhdGEuZnJhbWUodG1wKQ0KY29sbmFtZXModG1wX2RmKSA8LSBzZXEoMTpub19vZl9jbHVzdGVyKSAjIGFkZCBwcmVmaXggdG8gdGhlIGNvbHVtbiBuYW1lcw0KY29sbmFtZXModG1wX2RmKSA8LSBwYXN0ZTAoImsiLCBjb2xuYW1lcyh0bXBfZGYpKSANCg0KIyBnZXQgaW5kaXZpZHVhbCBQQ0ENCnRtcF9kZi5wY2EgPC0gcHJjb21wKHRtcF9kZiwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gRkFMU0UpDQoNCmluZC5jb29yZCA8LSB0bXBfZGYucGNhJHgNCmluZC5jb29yZCA8LSBpbmQuY29vcmRbLDE6Ml0NCg0KdG1wX2RmIDwtIGJpbmRfY29scyhhcy5kYXRhLmZyYW1lKHRtcF9kZiksIGFzLmRhdGEuZnJhbWUoaW5kLmNvb3JkKSkNCg0KY2x1c3RyZWUodG1wX2RmLCBwcmVmaXggPSAiayIpICMgcHJvZHVjZSBjbHVzdGVyaW5nIHRyZWVzLCBhIHZpc3VhbGl6YXRpb24gZm9yIGludGVycm9nYXRpbmcgY2x1c3RlcmluZyBhcyByZXNvbHV0aW9uIGluY3JlYXNlcw0KDQpvdmVybGF5X2xpc3QgPC0gY2x1c3RyZWVfb3ZlcmxheSh0bXBfZGYsIHByZWZpeCA9ICJrIiwgeF92YWx1ZSA9ICJQQzEiLCB5X3ZhbHVlID0gIlBDMiIsIHBsb3Rfc2lkZXMgPSBUUlVFKQ0Kb3ZlcmxheV9saXN0JG92ZXJsYXkNCm92ZXJsYXlfbGlzdCR4X3NpZGUNCm92ZXJsYXlfbGlzdCR5X3NpZGUNCmBgYA0KDQotLS0NCmNsVmFsaWQgdG8gY2hvb3NlIGJlc3QgY2x1c3RlcmluZyBhbGdvDQotLS0NCmh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS8xMC10aXBzLWZvci1jaG9vc2luZy10aGUtb3B0aW1hbC1udW1iZXItb2YtY2x1c3RlcnMtMjc3ZTkzZDcyZDkyDQpUaGUgY1ZhbGlkIHBhY2thZ2UgY2FuIGJlIHVzZWQgdG8gc2ltdWx0YW5lb3VzbHkgY29tcGFyZSBtdWx0aXBsZSBjbHVzdGVyaW5nIGFsZ29yaXRobXMsIHRvIGlkZW50aWZ5IHRoZSBiZXN0IGNsdXN0ZXJpbmcgYXBwcm9hY2ggYW5kIHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4gV2Ugd2lsbCBjb21wYXJlIGstbWVhbnMsIGhpZXJhcmNoaWNhbCBhbmQgUEFNIGNsdXN0ZXJpbmcuDQpDb25uZWN0aXZpdHkgYW5kIFNpbGhvdWV0dGUgYXJlIGJvdGggbWVhc3VyZW1lbnRzIG9mIGNvbm5lY3RlZG5lc3Mgd2hpbGUgdGhlIER1bm4gSW5kZXggaXMgdGhlIHJhdGlvIG9mIHRoZSBzbWFsbGVzdCBkaXN0YW5jZSBiZXR3ZWVuIG9ic2VydmF0aW9ucyBub3QgaW4gdGhlIHNhbWUgY2x1c3RlciB0byB0aGUgbGFyZ2VzdCBpbnRyYS1jbHVzdGVyIGRpc3RhbmNlLg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCmxpYnJhcnkoY2xWYWxpZCkgIyBTdGF0aXN0aWNhbCBhbmQgYmlvbG9naWNhbCB2YWxpZGF0aW9uIG9mIGNsdXN0ZXJpbmcgcmVzdWx0cy4gVGhpcyBwYWNrYWdlIGltcGxlbWVudHMgRHVubiBJbmRleCwgU2lsaG91ZXR0ZSwgQ29ubmVjdGl2aXR5LCBTdGFiaWxpdHksIEJISSBhbmQgQlNJDQpsaWJyYXJ5KG1jbHVzdCkgIyBCSUMgZm9yIHBhcmFtZXRlcml6ZWQgR2F1c3NpYW4gbWl4dHVyZSBtb2RlbHMgZml0dGVkIGJ5IEVNIGFsZ29yaXRobSBpbml0aWFsaXplZCBieSBtb2RlbC1iYXNlZCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KDQojIHNjYWxlX2RhdGEgPC0gYXMubWF0cml4KHQoc2NhbGUodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpKSkNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0Kc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pICMgd2l0aG91dCBzY2FsaW5nIHNpbmNlIHRoZSB2YXJpYWJsZXMgYWN0dWFsbHkgYXJlIG9uIHRoZSBzYW1lIHNjYWxlDQoNCmludGVybiA8LSBjbFZhbGlkKHNjYWxlX2RhdGEsIG5DbHVzdCA9IDI6OSwgDQogICAgICAgICAgICAgICAgICBjbE1ldGhvZHMgPSBjKCJoaWVyYXJjaGljYWwiLCAia21lYW5zIiwgImRpYW5hIiwgImZhbm55IiwgInNvbSIsICJtb2RlbCIsICJzb3RhIiwgInBhbSIsICJjbGFyYSIsICJhZ25lcyIpLA0KICAgICAgICAgICAgICAgICAgdmFsaWRhdGlvbiA9ICJpbnRlcm5hbCIpDQoNCnN1bW1hcnkoaW50ZXJuKQ0KYGBgDQoNCi0tLQ0KRXh0cmFjdGluZyBGZWF0dXJlcyBvZiBDbHVzdGVycw0KLS0tDQpodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vMTAtdGlwcy1mb3ItY2hvb3NpbmctdGhlLW9wdGltYWwtbnVtYmVyLW9mLWNsdXN0ZXJzLTI3N2U5M2Q3MmQ5Mg0KVWx0aW1hdGVseSwgd2Ugd291bGQgbGlrZSB0byBhbnN3ZXIgcXVlc3Rpb25zIGxpa2Ug4oCcd2hhdCBpcyBpdCB0aGF0IG1ha2VzIHRoaXMgY2x1c3RlciB1bmlxdWUgZnJvbSBvdGhlcnM/4oCdIGFuZCDigJx3aGF0IGFyZSB0aGUgY2x1c3RlcnMgdGhhdCBhcmUgc2ltaWxhciB0byBvbmUgYW5vdGhlcuKAnS4gTGV04oCZcyBzZWxlY3QgZm91ciBjbHVzdGVycyBhbmQgaW50ZXJyb2dhdGUgdGhlIGZlYXR1cmVzIG9mIHRoZXNlIGNsdXN0ZXJzLg0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCm5vX2sgPSAzDQpub192YXIgPSAzDQoNCiMgc2NhbGVfZGF0YSA8LSBhcy5tYXRyaXgodChzY2FsZSh0KGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkpKQ0KIyBzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChzY2FsZShkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQpzY2FsZV9kYXRhIDwtIGFzLm1hdHJpeChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkgIyB3aXRob3V0IHNjYWxpbmcgc2luY2UgdGhlIHZhcmlhYmxlcyBhY3R1YWxseSBhcmUgb24gdGhlIHNhbWUgc2NhbGUNCg0KIyBjb21wdXRlIGRpc3NpbWlsYXJpdHkgbWF0cml4IHdpdGggZXVjbGlkZWFuIGRpc3RhbmNlcw0KZCA8LSBkaXN0KHNjYWxlX2RhdGEsIG1ldGhvZCA9ICdldWNsaWRlYW4nKQ0KDQojIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHVzaW5nIFdhcmQncyBtZXRob2QNCnJlc19oYyA8LSBoY2x1c3QoZCwgbWV0aG9kID0gJ3dhcmQuRDInKQ0KDQojIGN1dCB0cmVlIGludG8gMyBncm91cHMNCmdycCA8LSBjdXRyZWUocmVzX2hjLCBrID0gbm9faykNCg0KIyB2aXN1YWxpemUNCnBsb3QocmVzX2hjLCBjZXggPSAwLjYpICMgcGxvdCB0cmVlDQpyZWN0LmhjbHVzdChyZXNfaGMsIGsgPSBub19rLCBib3JkZXIgPSAyOjUpICMgYWRkIHJlY3RhbmdsZXMNCg0KIyBleGVjdXRpb24gb2Ygay1tZWFucyB3aXRoIGsgPSA0DQpmaW5hbCA8LSBrbWVhbnMoc2NhbGVfZGF0YSwgbm9faywgbnN0YXJ0ID0gMzApDQoNCmZ2aXpfY2x1c3RlcihmaW5hbCwgZGF0YSA9IHNjYWxlX2RhdGEpICsgdGhlbWVfbWluaW1hbCgpICsgZ2d0aXRsZSgiayA9IDQiKQ0KYGBgDQpgYGB7cn0NCmFzLnRpYmJsZShzY2FsZV9kYXRhKSAlPiUgDQogIG11dGF0ZShjbHVzdGVyID0gZmluYWwkY2x1c3RlcikgJT4lDQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICBzdW1tYXJpc2VfYWxsKCdtZWFuJykNCg0KYXMudGliYmxlKHBsYXN0aWMpICU+JSANCiAgbXV0YXRlKGNsdXN0ZXIgPSBmaW5hbCRjbHVzdGVyKSAlPiUNCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogIHN1bW1hcmlzZV9hbGwoJ21lYW4nKQ0KYGBgDQpgYGB7cn0NCmRmIDwtIGFzX3RpYmJsZShzY2FsZV9kYXRhKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCkNCg0KY2x1c3Rlcl9wb3MgPC0gYXNfdGliYmxlKGZpbmFsJGNsdXN0ZXIpICU+JSByb3duYW1lc190b19jb2x1bW4oKQ0KY29sbmFtZXMoY2x1c3Rlcl9wb3MpIDwtIGMoInJvd25hbWUiLCAiY2x1c3RlciIpDQoNCmZpbmFsIDwtIGlubmVyX2pvaW4oY2x1c3Rlcl9wb3MsIGRmLCBieSA9ICJyb3duYW1lIikNCg0KbGlicmFyeShnZ2lyYXBoRXh0cmEpICMgZW5oYW5jZSAnZ2dwbG90MicgYW5kICdnZ2lyYXBoJywgcHJvdmlkZXMgZnVuY3Rpb25zIGZvciBleHBsb3JhdG9yeSBwbG90cywgc2VlIGh0dHBzOi8vcnB1YnMuY29tL2NhcmRpb21vb24vMjMxODIwDQoNCnJhZGFyIDwtIGdnUmFkYXIoZmluYWxbLTFdLCBhZXMoZ3JvdXAgPSBjbHVzdGVyKSwgcmVzY2FsZSA9IEZBTFNFLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHNpemUgPSAxLCBpbnRlcmFjdGl2ZSA9IEZBTFNFLCB1c2UubGFiZWwgPSBUUlVFKSArDQogIGZhY2V0X3dyYXAofmNsdXN0ZXIpICsNCiAgc2NhbGVfeV9kaXNjcmV0ZShicmVha3MgPSBOVUxMKSArICMgZG9uJ3Qgc2hvdyB0aWNrcw0KICB0aGVtZV9taW5pbWFsKCkNCg0KcmFkYXINCmBgYA0KYGBge3J9DQpkZiA8LSBhc190aWJibGUoc2NhbGVfZGF0YSkNCmRmJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGZpbmFsJGNsdXN0ZXIpDQoNCmxpYnJhcnkoR0dhbGx5KSAjIGV4dGVuZHMgZ2dwbG90MiBieSBhZGRpbmcgc2V2ZXJhbCBmdW5jdGlvbnMgdG8gcmVkdWNlIHRoZSBjb21wbGV4aXR5IG9mIGNvbWJpbmluZyBnZW9tcyB3aXRoIHRyYW5zZm9ybWVkIGRhdGENCg0KZ2dwYWlycyhkZiwgMTpub192YXIsIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoY29sb3IgPSBjbHVzdGVyLCBhbHBoYSA9IDAuNSksDQogICAgICAgIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJkZW5zaXR5RGlhZyIpKSwNCiAgICAgICAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCJwb2ludHMiLCBhbHBoYSA9IDAuOSkpKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQpgYGB7cn0NCmRmIDwtIGFzX3RpYmJsZShzY2FsZV9kYXRhKQ0KZGYkY2x1c3RlciA8LSBhcy5mYWN0b3IoZmluYWwkY2x1c3RlcikNCg0KYm94MSA8LSBnZ3Bsb3QoZGYsIGFlcyh4ID0gY2x1c3RlciwgeSA9IHByb2R1Y3QsIGNvbG91ciA9IGNsdXN0ZXIpKSArIA0KICAgIGdlb21fYm94cGxvdCgpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKyANCiAgICBnZ3RpdGxlKCJwcm9kdWN0IikNCmJveDEgPC0gZ2dwbG90bHkoYm94MSkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJwcm9kdWN0IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmJveDIgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsdXN0ZXIsIHkgPSByaWdpZCwgY29sb3VyID0gY2x1c3RlcikpICsgDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgZ2d0aXRsZSgicmlnaWQiKQ0KYm94MiA8LSBnZ3Bsb3RseShib3gyKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gInJpZ2lkIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmJveDMgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGNsdXN0ZXIsIHkgPSBmbGV4aWJsZSwgY29sb3VyID0gY2x1c3RlcikpICsgDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgZ2d0aXRsZSgiZmxleGlibGUiKQ0KYm94MyA8LSBnZ3Bsb3RseShib3gzKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gImZsZXhpYmxlIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCmZpZyA8LSBzdWJwbG90KGJveDEsIGJveDIsIGJveDMsIG1hcmdpbiA9IDApICU+JSBsYXlvdXQoKQ0KDQpmaWcNCmBgYA0KYGBge3J9DQpkZiA8LSBhc190aWJibGUoc2NhbGVfZGF0YSkNCmRmJGNsdXN0ZXIgPC0gYXMuZmFjdG9yKGZpbmFsJGNsdXN0ZXIpDQoNCmxpYnJhcnkoR0dhbGx5KSAjIGV4dGVuZHMgZ2dwbG90MiBieSBhZGRpbmcgc2V2ZXJhbCBmdW5jdGlvbnMgdG8gcmVkdWNlIHRoZSBjb21wbGV4aXR5IG9mIGNvbWJpbmluZyBnZW9tcyB3aXRoIHRyYW5zZm9ybWVkIGRhdGENCg0KIyBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL3BhcmFsbGVsLXBsb3QtZ2dhbGx5Lmh0bWwjY3VzdG9tDQpwYXJjb29yZF9wbG90IDwtIGdncGFyY29vcmQoZGYsDQogICAgICAgICAgIGNvbHVtbnMgPSAxOm5vX3ZhciwgZ3JvdXBDb2x1bW4gPSAobm9fdmFyKzEpLA0KICAgICAgICAgICBzY2FsZT0nY2VudGVyJywgIyBzY2FsaW5nOiBzdGFuZGFyZGl6ZSBhbmQgY2VudGVyIHZhcmlhYmxlcw0KICAgICAgICAgICBzaG93UG9pbnRzID0gVFJVRSwNCiAgICAgICAgICAgdGl0bGUgPSBuYW1lLA0KICAgICAgICAgICBhbHBoYUxpbmVzID0gMC4zKSArDQogIHRoZW1lX21pbmltYWwoKSANCnBhcmNvb3JkX3Bsb3QgPC0gZ2dwbG90bHkocGFyY29vcmRfcGxvdCkgJT4lIGxheW91dChhdXRvc2l6ZT1UKQ0KDQpwYXJjb29yZF9wbG90DQpgYGANCg0KLS0tDQpjb21wYXJlIHNjYWxlIGZ1bmN0aW9ucw0KLS0tDQpzY2FsZSBpcyBnZW5lcmljIGZ1bmN0aW9uIHdob3NlIGRlZmF1bHQgbWV0aG9kIGNlbnRlcnMgYW5kL29yIHNjYWxlcyB0aGUgY29sdW1ucyBvZiBhIG51bWVyaWMgbWF0cml4DQpub3JtYWxseSB5b3Ugc2NhbGUgdGhlIGNvbHVtbnMgKGllIHZhcmlhYmxlcykgdG8gaGF2ZSBhbGwgdmFyaWFibGVzIG9uIHRoZSBzYW1lIHNjYWxlIHN0IGFuIGluY3JlYXNlIGluIDEgdW5pdCBoYXMgdGhlIHNhbWUgbWVhbmluZyBhY3Jvc3MgYWxsIHZhcmlhYmxlcywgdGhlIHNoYXBlIC8gZGlzdHJpYnV0aW9uIHdpbGwgbm90IGNoYW5nZSwgYnV0IHRoZSBheGlzIHVuaXRzIChzZWUgc2NhdHRlciBwbG90cykNCnNjYWxpbmcgdGhlIHJvd3MgKGllIG9ic2VydmF0aW9ucykgbWFkZSBhbGwgb2JzZXJ2YXRpb25zIGVxdWFsIGluIHRoZSBzZW5zZSB0aGF0IHRoZSBhYnNvbHV0ZSBkaXN0YW5jZXMgd2VyZSByZXBsYWNlZCBieSByZWxhdGl2ZSBkaXN0YW5jZXMgYWNyb3NzIGFsbCBvYnNlcnZhdGlvbnMsIHN0IG5vdyB0aGUgYmlnZ2VzdCB2YWx1ZSBpbiBhIHJvdyB3YXMgYWNyb3NzIGFsbCByb3dzIGVxdWFsbHkgYmlnIChzZWUgImRhdGFmcmFtZSIgcGxvdCksDQppbiBvdXIgY2FzZSB0aGF0IHdhcyB0aGUgcHJvZHVjdGlvbiB2YXJpYWJsZSwgbm93IHJpZ2lkIGFuZCBmbGV4aWJsZSB3ZXJlIHNlcGFyYXRlZCBpZiB0aGVyZSB3YXMgbW9yZSBvciBsZXNzIG9mIHRoZW0gcmVsYXRpdmUgdG8gdGhlIHRvdGFsIHByb2R1Y3Rpb24gKGJpZ2dlc3QgdmFsdWUsIHdlbGwgYWN0dWFsbHkgdG8gdGhlIG1lYW4gbm90IHRoZSBtYXgsIGJ1dCBuZXZlciBtaW5kKSwgdGhpcyBzb3J0ZWQgYWxsIG9ic2VydmF0aW9ucyBhY2NvcmRpbmcgdG8gYmUgbW9yZSBvbiB0aGUgcmlnaWQgb3IgZmxleGlibGUgc2lkZSwNCmFuZCB0aGF0IHdhcyBleGNhdGx5IHdoYXQgSSB3YXMgbG9va2luZyBmb3IgKGFuZCB3aHkgSSB3YXMgdG9vIGhhcHB5IHRvIHNlZSBteSBtaXN0YWtlIGluIHRoZSBmaXJzdCBwbGFjZSkNCg0KYGBge3J9DQpuYW1lID0gYygncG9seW1lcl9wcm9kdWNlcicpDQpkZiA8LSBwbGFzdGljICU+JSBzZWxlY3QoLSB0b3RhbF93YXN0ZV9kaXZfcHJvZHVjdGlvbiwgLXJhbmssIC1ub19vZl9hc3NldHMsIC10b3RhbF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKSAlPiUgIyByZW1vdmVkIHZhcmlhYmxlcyB3aGljaCBhcmUgZGVwZW5kZWQgb24gZWFjaCBvdGhlcg0KICByZW5hbWUoIHByb2R1Y3QgPSBwcm9kdWN0aW9uX29mX2luX3Njb3BlX3BvbHltZXJzLCANCiAgICAgICAgICBmbGV4aWJsZSA9IGZsZXhpYmxlX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlLCANCiAgICAgICAgICByaWdpZCA9IHJpZ2lkX2Zvcm1hdF9jb250cmlidXRpb25fdG9fc3VwX3dhc3RlKQ0KDQpzY2FsZV9kYXRhIDwtIGFzX3RpYmJsZSh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpICMgV2UgbmVlZCB0byBub3JtYWxpemUgdGhlIGRhdGEgYmFzZWQgb24gc2NhbGUgZnVuY3Rpb24gYmVjYXVzZSB0aGUgdmFyaWFibGVzIGFyZSBkaWZmZXJlbnQgc2NhbGVzOyBOb3JtYWxpemF0aW9uIG1lYW5zIHN1YnRyYWN0aW5nIG1lYW4gZnJvbSBlYWNoIG9ic2VydmF0aW9uIGFuZCBkaXZpZGluZyB3aXRoIHN0YW5kYXJkIGRldmlhdGlvbi4gQ2hlY2sgYWxsIHRoZSB2YXJpYWJsZXMgbWVhbiB2YWx1ZXMgYXJlIHplcm8gbm93DQoNCmhlYWQoc2NhbGVfZGF0YSkNCg0Kc2NhbGVfZGF0YSA8LSBhc190aWJibGUoc2NhbGUoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKQ0KDQpoZWFkKHNjYWxlX2RhdGEpDQoNCmhlYWQodChkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSkpDQoNCmxpYnJhcnkoY2FyZXQpDQpwcmVPYmogPC0gcHJlUHJvY2VzcyhkZlssICFuYW1lcyhkZikgJWluJSBuYW1lXSwgbWV0aG9kPWMoImNlbnRlciIsICJzY2FsZSIpKQ0KbmV3RGF0YSA8LSBwcmVkaWN0KHByZU9iaiwgZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pDQpoZWFkKG5ld0RhdGEpDQoNCiMgZmluZCBtb3JlIG1ldGhvZHMgaGVyZTogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTUyMTU0NTcvc3RhbmRhcmRpemUtZGF0YS1jb2x1bW5zLWluLXINCmBgYA0KDQpgYGB7cn0NCm5hbWUgPSBjKCdwb2x5bWVyX3Byb2R1Y2VyJykNCmRmIDwtIHBsYXN0aWMgJT4lIHNlbGVjdCgtIHRvdGFsX3dhc3RlX2Rpdl9wcm9kdWN0aW9uLCAtcmFuaywgLW5vX29mX2Fzc2V0cywgLXRvdGFsX2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpICU+JSAjIHJlbW92ZWQgdmFyaWFibGVzIHdoaWNoIGFyZSBkZXBlbmRlZCBvbiBlYWNoIG90aGVyDQogIHJlbmFtZSggcHJvZHVjdCA9IHByb2R1Y3Rpb25fb2ZfaW5fc2NvcGVfcG9seW1lcnMsIA0KICAgICAgICAgIGZsZXhpYmxlID0gZmxleGlibGVfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUsIA0KICAgICAgICAgIHJpZ2lkID0gcmlnaWRfZm9ybWF0X2NvbnRyaWJ1dGlvbl90b19zdXBfd2FzdGUpDQoNCnNjYWxlUm93IDwtIGFzX3RpYmJsZSh0KHNjYWxlKHQoZGZbLCAhbmFtZXMoZGYpICVpbiUgbmFtZV0pKSkpDQpoZWFkKHNjYWxlUm93KQ0Kc2NhbGVDb2wgPC0gYXNfdGliYmxlKHNjYWxlKGRmWywgIW5hbWVzKGRmKSAlaW4lIG5hbWVdKSkNCmhlYWQoc2NhbGVDb2wpDQpkZiA8LSBkZiAlPiUgc2VsZWN0KC1wb2x5bWVyX3Byb2R1Y2VyKQ0KaGVhZChkZikNCg0Kc2NhdHRlclJvdyA8LSBnZ3Bsb3Qoc2NhbGVSb3csIGFlcyh4ID0gZmxleGlibGUsIHkgPSByaWdpZCkpICsNCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC43NSkgKyAjIHBvaW50IGdlb20gaXMgdXNlZCB0byBjcmVhdGUgc2NhdHRlcnBsb3RzDQogICAgdGhlbWVfbWluaW1hbCgpIA0Kc2NhdHRlclJvdyA8LSBnZ3Bsb3RseShzY2F0dGVyUm93KSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gInNjYWxlUm93IiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnNjYXR0ZXJDb2wgPC0gZ2dwbG90KHNjYWxlQ29sLCBhZXMoeCA9IGZsZXhpYmxlLCB5ID0gcmlnaWQpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJDb2wgPC0gZ2dwbG90bHkoc2NhdHRlckNvbCkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZUNvbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzY2F0dGVyRGYgPC0gZ2dwbG90KGRmLCBhZXMoeCA9IGZsZXhpYmxlLCB5ID0gcmlnaWQpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJEZiA8LSBnZ3Bsb3RseShzY2F0dGVyRGYpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAibm9ybWFsIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnN1YnBsb3Qoc2NhdHRlclJvdywgc2NhdHRlckNvbCwgc2NhdHRlckRmKSAlPiUgbGF5b3V0KCkNCg0KDQpzY2FsZVJvdzIgPC0gc2NhbGVSb3cgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhmbGV4aWJsZSxyaWdpZCxwcm9kdWN0KSkNCmJveFJvdyA8LSBnZ3Bsb3Qoc2NhbGVSb3cyLCBhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3VyID0gbmFtZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KYm94Um93IDwtIGdncGxvdGx5KGJveFJvdykgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZVJvdyIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzY2FsZUNvbDIgPC0gc2NhbGVDb2wgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gYyhmbGV4aWJsZSxyaWdpZCxwcm9kdWN0KSkNCmJveENvbCA8LSBnZ3Bsb3Qoc2NhbGVDb2wyLCBhZXMoeCA9IG5hbWUsIHkgPSB2YWx1ZSwgY29sb3VyID0gbmFtZSkpICsNCiAgICBnZW9tX2JveHBsb3QoKSArDQogICAgdGhlbWVfbWluaW1hbCgpIA0KYm94Q29sIDwtIGdncGxvdGx5KGJveENvbCkgJT4lIGxheW91dChhbm5vdGF0aW9ucyA9IGxpc3QodGV4dCA9ICJzY2FsZUNvbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpkZl9sb25nIDwtIGRmICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpib3hEZiA8LSBnZ3Bsb3QoZGZfbG9uZywgYWVzKHggPSBuYW1lLCB5ID0gdmFsdWUsIGNvbG91ciA9IG5hbWUpKSArDQogICAgZ2VvbV9ib3hwbG90KCkgKw0KICAgIHRoZW1lX21pbmltYWwoKSANCmJveERmIDwtIGdncGxvdGx5KGJveERmKSAlPiUgbGF5b3V0KGFubm90YXRpb25zID0gbGlzdCh0ZXh0ID0gIm5vcm1hbCIsIGZvbnQgPSBmLCB4cmVmID0gInBhcGVyIiwgeXJlZiA9ICJwYXBlciIsIHlhbmNob3IgPSAiYm90dG9tIiwgeGFuY2hvciA9ICJjZW50ZXIiLCBhbGlnbiA9ICJjZW50ZXIiLCB4ID0gMC41LCB5ID0gMSwgc2hvd2Fycm93ID0gRkFMU0UpKQ0KDQpzdWJwbG90KGJveFJvdywgYm94Q29sLCBib3hEZikgJT4lIGxheW91dCgpDQoNCg0Kc2NhbGVSb3cgPC0gc2NhbGVSb3cgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyUm93IDwtIGdncGxvdChzY2FsZVJvdywgYWVzKHggPSB2YWx1ZSwgeSA9IHJvd2lkLCBjb2xvdXIgPSBuYW1lKSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkgDQpzY2F0dGVyUm93IDwtIGdncGxvdGx5KHNjYXR0ZXJSb3cpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAic2NhbGVSb3ciLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCg0Kc2NhbGVDb2wgPC0gc2NhbGVDb2wgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyQ29sIDwtIGdncGxvdChzY2FsZUNvbCwgYWVzKHggPSB2YWx1ZSwgeSA9IHJvd2lkLCBjb2xvdXIgPSBuYW1lKSkgKw0KICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjc1KSArICMgcG9pbnQgZ2VvbSBpcyB1c2VkIHRvIGNyZWF0ZSBzY2F0dGVycGxvdHMNCiAgICB0aGVtZV9taW5pbWFsKCkgDQpzY2F0dGVyQ29sIDwtIGdncGxvdGx5KHNjYXR0ZXJDb2wpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAic2NhbGVDb2wiLCBmb250ID0gZiwgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCB5YW5jaG9yID0gImJvdHRvbSIsIHhhbmNob3IgPSAiY2VudGVyIiwgYWxpZ24gPSAiY2VudGVyIiwgeCA9IDAuNSwgeSA9IDEsIHNob3dhcnJvdyA9IEZBTFNFKSkNCg0KZGZfaWQgPC0gZGYgJT4lIHJvd2lkX3RvX2NvbHVtbigpICU+JSBwaXZvdF9sb25nZXIoY29scyA9IGMoZmxleGlibGUscmlnaWQscHJvZHVjdCkpDQpzY2F0dGVyRGYgPC0gZ2dwbG90KGRmX2lkLCBhZXMoeCA9IHZhbHVlLCB5ID0gcm93aWQsIGNvbG91ciA9IG5hbWUpKSArDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNzUpICsgIyBwb2ludCBnZW9tIGlzIHVzZWQgdG8gY3JlYXRlIHNjYXR0ZXJwbG90cw0KICAgIHRoZW1lX21pbmltYWwoKSANCnNjYXR0ZXJEZiA8LSBnZ3Bsb3RseShzY2F0dGVyRGYpICU+JSBsYXlvdXQoYW5ub3RhdGlvbnMgPSBsaXN0KHRleHQgPSAibm9ybWFsIiwgZm9udCA9IGYsIHhyZWYgPSAicGFwZXIiLCB5cmVmID0gInBhcGVyIiwgeWFuY2hvciA9ICJib3R0b20iLCB4YW5jaG9yID0gImNlbnRlciIsIGFsaWduID0gImNlbnRlciIsIHggPSAwLjUsIHkgPSAxLCBzaG93YXJyb3cgPSBGQUxTRSkpDQoNCnN1YnBsb3Qoc2NhdHRlclJvdywgc2NhdHRlckNvbCwgc2NhdHRlckRmKSAlPiUgbGF5b3V0KCkNCmBgYA0KDQo=